AmbientShadow.cpp revision 726118b35240957710d4d85fb5747e2ba8b934f7
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "OpenGLRenderer" 18 19#include <math.h> 20#include <utils/Log.h> 21#include <utils/Vector.h> 22 23#include "AmbientShadow.h" 24#include "ShadowTessellator.h" 25#include "Vertex.h" 26 27namespace android { 28namespace uirenderer { 29 30/** 31 * Calculate the shadows as a triangle strips while alpha value as the 32 * shadow values. 33 * 34 * @param vertices The shadow caster's polygon, which is represented in a Vector3 35 * array. 36 * @param vertexCount The length of caster's polygon in terms of number of 37 * vertices. 38 * @param centroid3d The centroid of the shadow caster. 39 * @param heightFactor The factor showing the higher the object, the lighter the 40 * shadow. 41 * @param geomFactor The factor scaling the geometry expansion along the normal. 42 * 43 * @param shadowVertexBuffer Return an floating point array of (x, y, a) 44 * triangle strips mode. 45 */ 46void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount, 47 const Vector3& centroid3d, float heightFactor, float geomFactor, 48 VertexBuffer& shadowVertexBuffer) { 49 const int rays = SHADOW_RAY_COUNT; 50 // Validate the inputs. 51 if (vertexCount < 3 || heightFactor <= 0 || rays <= 0 52 || geomFactor <= 0) { 53#if DEBUG_SHADOW 54 ALOGE("Invalid input for createAmbientShadow(), early return!"); 55#endif 56 return; 57 } 58 59 Vector<Vector2> dir; // TODO: use C++11 unique_ptr 60 dir.setCapacity(rays); 61 float rayDist[rays]; 62 float rayHeight[rays]; 63 calculateRayDirections(rays, dir.editArray()); 64 65 // Calculate the length and height of the points along the edge. 66 // 67 // The math here is: 68 // Intersect each ray (starting from the centroid) with the polygon. 69 for (int i = 0; i < rays; i++) { 70 int edgeIndex; 71 float edgeFraction; 72 float rayDistance; 73 calculateIntersection(vertices, vertexCount, centroid3d, dir[i], edgeIndex, 74 edgeFraction, rayDistance); 75 rayDist[i] = rayDistance; 76 if (edgeIndex < 0 || edgeIndex >= vertexCount) { 77#if DEBUG_SHADOW 78 ALOGE("Invalid edgeIndex!"); 79#endif 80 edgeIndex = 0; 81 } 82 float h1 = vertices[edgeIndex].z; 83 float h2 = vertices[((edgeIndex + 1) % vertexCount)].z; 84 rayHeight[i] = h1 + edgeFraction * (h2 - h1); 85 } 86 87 // The output buffer length basically is roughly rays * layers, but since we 88 // need triangle strips, so we need to duplicate vertices to accomplish that. 89 AlphaVertex* shadowVertices = shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT); 90 91 // Calculate the vertex of the shadows. 92 // 93 // The math here is: 94 // Along the edges of the polygon, for each intersection point P (generated above), 95 // calculate the normal N, which should be perpendicular to the edge of the 96 // polygon (represented by the neighbor intersection points) . 97 // Shadow's vertices will be generated as : P + N * scale. 98 for (int rayIndex = 0; rayIndex < rays; rayIndex++) { 99 Vector2 normal(1.0f, 0.0f); 100 calculateNormal(rays, rayIndex, dir.array(), rayDist, normal); 101 102 // The vertex should be start from rayDist[i] then scale the 103 // normalizeNormal! 104 Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] + 105 Vector2(centroid3d.x, centroid3d.y); 106 107 // outer ring of points, expanded based upon height of each ray intersection 108 float expansionDist = rayHeight[rayIndex] * heightFactor * 109 geomFactor; 110 AlphaVertex::set(&shadowVertices[rayIndex], 111 intersection.x + normal.x * expansionDist, 112 intersection.y + normal.y * expansionDist, 113 0.0f); 114 115 // inner ring of points 116 float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor); 117 AlphaVertex::set(&shadowVertices[rayIndex + rays], 118 intersection.x, 119 intersection.y, 120 opacity); 121 } 122 float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor); 123 AlphaVertex::set(&shadowVertices[SHADOW_VERTEX_COUNT - 1], 124 centroid3d.x, centroid3d.y, centroidAlpha); 125 126#if DEBUG_SHADOW 127 if (currentVertexIndex != SHADOW_VERTEX_COUNT) { 128 ALOGE("number of vertex generated for ambient shadow is wrong! " 129 "current: %d , expected: %d", currentVertexIndex, SHADOW_VERTEX_COUNT); 130 } 131 for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) { 132 ALOGD("ambient shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x, 133 shadowVertices[i].y, shadowVertices[i].alpha); 134 } 135#endif 136} 137 138/** 139 * Generate an array of rays' direction vectors. 140 * 141 * @param rays The number of rays shooting out from the centroid. 142 * @param dir Return the array of ray vectors. 143 */ 144void AmbientShadow::calculateRayDirections(int rays, Vector2* dir) { 145 float deltaAngle = 2 * M_PI / rays; 146 147 for (int i = 0; i < rays; i++) { 148 dir[i].x = sinf(deltaAngle * i); 149 dir[i].y = cosf(deltaAngle * i); 150 } 151} 152 153/** 154 * Calculate the intersection of a ray hitting the polygon. 155 * 156 * @param vertices The shadow caster's polygon, which is represented in a 157 * Vector3 array. 158 * @param vertexCount The length of caster's polygon in terms of number of vertices. 159 * @param start The starting point of the ray. 160 * @param dir The direction vector of the ray. 161 * 162 * @param outEdgeIndex Return the index of the segment (or index of the starting 163 * vertex) that ray intersect with. 164 * @param outEdgeFraction Return the fraction offset from the segment starting 165 * index. 166 * @param outRayDist Return the ray distance from centroid to the intersection. 167 */ 168void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount, 169 const Vector3& start, const Vector2& dir, int& outEdgeIndex, 170 float& outEdgeFraction, float& outRayDist) { 171 float startX = start.x; 172 float startY = start.y; 173 float dirX = dir.x; 174 float dirY = dir.y; 175 // Start the search from the last edge from poly[len-1] to poly[0]. 176 int p1 = vertexCount - 1; 177 178 for (int p2 = 0; p2 < vertexCount; p2++) { 179 float p1x = vertices[p1].x; 180 float p1y = vertices[p1].y; 181 float p2x = vertices[p2].x; 182 float p2y = vertices[p2].y; 183 184 // The math here is derived from: 185 // f(t, v) = p1x * (1 - t) + p2x * t - (startX + dirX * v) = 0; 186 // g(t, v) = p1y * (1 - t) + p2y * t - (startY + dirY * v) = 0; 187 float div = (dirX * (p1y - p2y) + dirY * p2x - dirY * p1x); 188 if (div != 0) { 189 float t = (dirX * (p1y - startY) + dirY * startX - dirY * p1x) / (div); 190 if (t > 0 && t <= 1) { 191 float t2 = (p1x * (startY - p2y) 192 + p2x * (p1y - startY) 193 + startX * (p2y - p1y)) / div; 194 if (t2 > 0) { 195 outEdgeIndex = p1; 196 outRayDist = t2; 197 outEdgeFraction = t; 198 return; 199 } 200 } 201 } 202 p1 = p2; 203 } 204 return; 205}; 206 207/** 208 * Calculate the normal at the intersection point between a ray and the polygon. 209 * 210 * @param rays The total number of rays. 211 * @param currentRayIndex The index of the ray which the normal is based on. 212 * @param dir The array of the all the rays directions. 213 * @param rayDist The pre-computed ray distances array. 214 * 215 * @param normal Return the normal. 216 */ 217void AmbientShadow::calculateNormal(int rays, int currentRayIndex, 218 const Vector2* dir, const float* rayDist, Vector2& normal) { 219 int preIndex = (currentRayIndex - 1 + rays) % rays; 220 int postIndex = (currentRayIndex + 1) % rays; 221 Vector2 p1 = dir[preIndex] * rayDist[preIndex]; 222 Vector2 p2 = dir[postIndex] * rayDist[postIndex]; 223 224 // Now the V (deltaX, deltaY) is the vector going CW around the poly. 225 Vector2 delta = p2 - p1; 226 if (delta.length() != 0) { 227 delta.normalize(); 228 // Calculate the normal , which is CCW 90 rotate to the V. 229 // 90 degrees CCW about z-axis: (x, y, z) -> (-y, x, z) 230 normal.x = -delta.y; 231 normal.y = delta.x; 232 } 233} 234 235}; // namespace uirenderer 236}; // namespace android 237