AmbientShadow.cpp revision 63d41abb40b3ce40d8b9bccb1cf186e8158a3687
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 const int layers = SHADOW_LAYER_COUNT; 51 // Validate the inputs. 52 if (vertexCount < 3 || heightFactor <= 0 || layers <= 0 || rays <= 0 53 || geomFactor <= 0) { 54#if DEBUG_SHADOW 55 ALOGE("Invalid input for createAmbientShadow(), early return!"); 56#endif 57 return; 58 } 59 60 Vector<Vector2> dir; // TODO: use C++11 unique_ptr 61 dir.setCapacity(rays); 62 float rayDist[rays]; 63 float rayHeight[rays]; 64 calculateRayDirections(rays, dir.editArray()); 65 66 // Calculate the length and height of the points along the edge. 67 // 68 // The math here is: 69 // Intersect each ray (starting from the centroid) with the polygon. 70 for (int i = 0; i < rays; i++) { 71 int edgeIndex; 72 float edgeFraction; 73 float rayDistance; 74 calculateIntersection(vertices, vertexCount, centroid3d, dir[i], edgeIndex, 75 edgeFraction, rayDistance); 76 rayDist[i] = rayDistance; 77 if (edgeIndex < 0 || edgeIndex >= vertexCount) { 78#if DEBUG_SHADOW 79 ALOGE("Invalid edgeIndex!"); 80#endif 81 edgeIndex = 0; 82 } 83 float h1 = vertices[edgeIndex].z; 84 float h2 = vertices[((edgeIndex + 1) % vertexCount)].z; 85 rayHeight[i] = h1 + edgeFraction * (h2 - h1); 86 } 87 88 // The output buffer length basically is roughly rays * layers, but since we 89 // need triangle strips, so we need to duplicate vertices to accomplish that. 90 AlphaVertex* shadowVertices = shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT); 91 92 // Calculate the vertex of the shadows. 93 // 94 // The math here is: 95 // Along the edges of the polygon, for each intersection point P (generated above), 96 // calculate the normal N, which should be perpendicular to the edge of the 97 // polygon (represented by the neighbor intersection points) . 98 // Shadow's vertices will be generated as : P + N * scale. 99 int currentVertexIndex = 0; 100 for (int layerIndex = 0; layerIndex <= layers; layerIndex++) { 101 for (int rayIndex = 0; rayIndex < rays; rayIndex++) { 102 103 Vector2 normal(1.0f, 0.0f); 104 calculateNormal(rays, rayIndex, dir.array(), rayDist, normal); 105 106 float opacity = 1.0 / (1 + rayHeight[rayIndex] / heightFactor); 107 108 // The vertex should be start from rayDist[i] then scale the 109 // normalizeNormal! 110 Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] + 111 Vector2(centroid3d.x, centroid3d.y); 112 113 float layerRatio = layerIndex / (float)(layers); 114 // The higher the intersection is, the further the ambient shadow expanded. 115 float expansionDist = rayHeight[rayIndex] / heightFactor * 116 geomFactor * (1 - layerRatio); 117 AlphaVertex::set(&shadowVertices[currentVertexIndex++], 118 intersection.x + normal.x * expansionDist, 119 intersection.y + normal.y * expansionDist, 120 layerRatio * opacity); 121 } 122 123 } 124 float centroidAlpha = 1.0 / (1 + centroid3d.z / heightFactor); 125 AlphaVertex::set(&shadowVertices[currentVertexIndex++], 126 centroid3d.x, centroid3d.y, centroidAlpha); 127 128#if DEBUG_SHADOW 129 if (currentVertexIndex != SHADOW_VERTEX_COUNT) { 130 ALOGE("number of vertex generated for ambient shadow is wrong! " 131 "current: %d , expected: %d", currentVertexIndex, SHADOW_VERTEX_COUNT); 132 } 133 for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) { 134 ALOGD("ambient shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x, 135 shadowVertices[i].y, shadowVertices[i].alpha); 136 } 137#endif 138} 139 140/** 141 * Generate an array of rays' direction vectors. 142 * 143 * @param rays The number of rays shooting out from the centroid. 144 * @param dir Return the array of ray vectors. 145 */ 146void AmbientShadow::calculateRayDirections(int rays, Vector2* dir) { 147 float deltaAngle = 2 * M_PI / rays; 148 149 for (int i = 0; i < rays; i++) { 150 dir[i].x = sinf(deltaAngle * i); 151 dir[i].y = cosf(deltaAngle * i); 152 } 153} 154 155/** 156 * Calculate the intersection of a ray hitting the polygon. 157 * 158 * @param vertices The shadow caster's polygon, which is represented in a 159 * Vector3 array. 160 * @param vertexCount The length of caster's polygon in terms of number of vertices. 161 * @param start The starting point of the ray. 162 * @param dir The direction vector of the ray. 163 * 164 * @param outEdgeIndex Return the index of the segment (or index of the starting 165 * vertex) that ray intersect with. 166 * @param outEdgeFraction Return the fraction offset from the segment starting 167 * index. 168 * @param outRayDist Return the ray distance from centroid to the intersection. 169 */ 170void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount, 171 const Vector3& start, const Vector2& dir, int& outEdgeIndex, 172 float& outEdgeFraction, float& outRayDist) { 173 float startX = start.x; 174 float startY = start.y; 175 float dirX = dir.x; 176 float dirY = dir.y; 177 // Start the search from the last edge from poly[len-1] to poly[0]. 178 int p1 = vertexCount - 1; 179 180 for (int p2 = 0; p2 < vertexCount; p2++) { 181 float p1x = vertices[p1].x; 182 float p1y = vertices[p1].y; 183 float p2x = vertices[p2].x; 184 float p2y = vertices[p2].y; 185 186 // The math here is derived from: 187 // f(t, v) = p1x * (1 - t) + p2x * t - (startX + dirX * v) = 0; 188 // g(t, v) = p1y * (1 - t) + p2y * t - (startY + dirY * v) = 0; 189 float div = (dirX * (p1y - p2y) + dirY * p2x - dirY * p1x); 190 if (div != 0) { 191 float t = (dirX * (p1y - startY) + dirY * startX - dirY * p1x) / (div); 192 if (t > 0 && t <= 1) { 193 float t2 = (p1x * (startY - p2y) 194 + p2x * (p1y - startY) 195 + startX * (p2y - p1y)) / div; 196 if (t2 > 0) { 197 outEdgeIndex = p1; 198 outRayDist = t2; 199 outEdgeFraction = t; 200 return; 201 } 202 } 203 } 204 p1 = p2; 205 } 206 return; 207}; 208 209/** 210 * Calculate the normal at the intersection point between a ray and the polygon. 211 * 212 * @param rays The total number of rays. 213 * @param currentRayIndex The index of the ray which the normal is based on. 214 * @param dir The array of the all the rays directions. 215 * @param rayDist The pre-computed ray distances array. 216 * 217 * @param normal Return the normal. 218 */ 219void AmbientShadow::calculateNormal(int rays, int currentRayIndex, 220 const Vector2* dir, const float* rayDist, Vector2& normal) { 221 int preIndex = (currentRayIndex - 1 + rays) % rays; 222 int postIndex = (currentRayIndex + 1) % rays; 223 Vector2 p1 = dir[preIndex] * rayDist[preIndex]; 224 Vector2 p2 = dir[postIndex] * rayDist[postIndex]; 225 226 // Now the V (deltaX, deltaY) is the vector going CW around the poly. 227 Vector2 delta = p2 - p1; 228 if (delta.length() != 0) { 229 delta.normalize(); 230 // Calculate the normal , which is CCW 90 rotate to the V. 231 // 90 degrees CCW about z-axis: (x, y, z) -> (-y, x, z) 232 normal.x = -delta.y; 233 normal.y = delta.x; 234 } 235} 236 237}; // namespace uirenderer 238}; // namespace android 239