/// <summary> /// Updates the shadow transformation and state. /// </summary> /// <param name="frustumPlanes"> /// The frustum planes used for culling the shadow. /// </param> /// <param name="force"> /// Whether to force the update, even if it is not needed. /// </param> /// <returns> /// The <see cref="RecalculateShadowResult"/> indicating the /// shadow behaviour change during calculation. /// </returns> public RecalculateShadowResult RecalculateShadow(Plane[] frustumPlanes, bool force) { #if UNITY_EDITOR if (_isDestroyed) { return(RecalculateShadowResult.Skipped); } #endif _isVisible = _isStatic; // Determine whether the owner GameObject changed the active state // and react correspondingly bool isGameObjectActive = gameObject.activeInHierarchy; if (isGameObjectActive != _isGameObjectActivePrev) { _isGameObjectActivePrev = isGameObjectActive; if (isGameObjectActive) { RegisterShadow(); return(RecalculateShadowResult.ChangedManager); } UnregisterShadow(); return(RecalculateShadowResult.ChangedManager); } _isGameObjectActivePrev = isGameObjectActive; if (!isGameObjectActive) { return(RecalculateShadowResult.Skipped); } // Updating the transform state (position and forward vectors) // Determine whether the transform has moved Vector3 transformPosition = _transform.position; Quaternion transformRotation = _transform.rotation; Vector3 transformForward = transformRotation * kVector3Forward; bool transformChanged = false; if (_autoStaticTime > 0f) { if (transformPosition.x != _transformPositionPrev.x || transformPosition.y != _transformPositionPrev.y || transformPosition.z != _transformPositionPrev.z || transformForward.x != _transformForwardPrev.x || transformForward.y != _transformForwardPrev.y || transformForward.z != _transformForwardPrev.z ) { _autoStaticTimeCounter = 0f; transformChanged = true; } _transformPositionPrev = transformPosition; _transformForwardPrev = transformForward; } float deltaTime = Time.deltaTime; if (!_isFirstCalculation) { // If we have AutoStatic #if UNITY_EDITOR if (Application.isPlaying) { #endif if (_autoStaticTime > 0f) { // If the object has moved - remove the shadow // from static manager and move to non-static if (_isStatic && transformChanged) { UnregisterShadow(); _isStatic = false; RegisterShadow(); return(RecalculateShadowResult.ChangedManager); } // If the object hasn't moved for AutoStaticTime seconds, // then mark it as static _autoStaticTimeCounter += deltaTime; if (!_isStatic && _autoStaticTimeCounter > _autoStaticTime) { UnregisterShadow(); _isStatic = true; RegisterShadow(); return(RecalculateShadowResult.ChangedManager); } } // Do not update static shadows by default if (_isStatic && !force) { return(RecalculateShadowResult.Skipped); } #if UNITY_EDITOR } #endif } // Is this our first update? _isFirstCalculation = false; // Determine the light source position bool useLightSource = _lightVectorSource == LightVectorSourceEnum.GameObject && _lightSourceObject != null; Vector3 lightSourcePosition; if (useLightSource) { lightSourcePosition = _lightSourceObject.position; } else { lightSourcePosition.x = 0f; lightSourcePosition.y = 0f; lightSourcePosition.z = 0f; } // The actual light direction vector that'll be used Vector3 actualLightVector; if (useLightSource) { if (_lightSourceObjectIsDirectionalLight) { actualLightVector = _lightSourceObject.rotation * kVector3Forward; } else { actualLightVector.x = transformPosition.x - lightSourcePosition.x; actualLightVector.y = transformPosition.y - lightSourcePosition.y; actualLightVector.z = transformPosition.z - lightSourcePosition.z; actualLightVector = actualLightVector.FastNormalized(); } } else { actualLightVector = _lightVector; } _actualLightVectorPrev = actualLightVector; // Do a raycast from transform.position to the center of the shadow RaycastHit hitInfo; bool raycastResult = Physics.Raycast(transformPosition, actualLightVector, out hitInfo, _projectionDistance, _layerMaskInt); if (raycastResult) { Vector3 hitPoint = hitInfo.point; // Scale the shadow respectively Vector3 lossyScale = _transform.lossyScale; float scaledDoubleShadowSize = Mathf.Max(Mathf.Max(lossyScale.x, lossyScale.y), lossyScale.z) * _shadowSize; // Distance scale float distance = hitInfo.distance; float scaleWeight = (distance - _shadowSizeScaleStartDistance) / (_shadowSizeScaleEndDistance - _shadowSizeScaleStartDistance); scaleWeight = scaleWeight > 1f ? 1f : (scaleWeight < 0f ? 0f : scaleWeight); if (scaleWeight != 0f) { float scale = _shadowSizeEndScale + (1f - _shadowSizeEndScale) * (1f - scaleWeight); scaledDoubleShadowSize *= scale; } float scaledShadowSize = scaledDoubleShadowSize * 0.5f; if (!_isStatic && _cullInvisible) { // We can calculate approximate bounds for orthographic shadows easily // and cull shadows based on these bounds and camera frustum if (!_isPerspectiveProjection) { Vector3 shadowScale; shadowScale.x = scaledShadowSize; shadowScale.y = scaledShadowSize; shadowScale.z = scaledShadowSize; Bounds bounds = new Bounds(); bounds.center = hitPoint; bounds.extents = shadowScale; _isVisible = GeometryUtility.TestPlanesAABB(frustumPlanes, bounds); if (!_isVisible) { return(RecalculateShadowResult.Skipped); } } else { // For perspective shadows, we can at least try to // not draw shadows that fall on invisible objects Bounds rendererBounds; bool result = ShadowManager.Instance.ColliderToRendererBoundsCache.GetRendererBoundsFromCollider(hitInfo.collider, out rendererBounds); if (result) { _isVisible = GeometryUtility.TestPlanesAABB(frustumPlanes, rendererBounds); if (!_isVisible) { return(RecalculateShadowResult.Skipped); } } } } // Calculate angle from light direction vector to surface normal if (_isSmoothRotation) { _normal = Vector3.Lerp(_normal, hitInfo.normal, _smoothRotationSpeed * deltaTime).FastNormalized(); } else { _normal = hitInfo.normal; } float lightVectorToNormalDot = -actualLightVector.x * _normal.x - actualLightVector.y * _normal.y - actualLightVector.z * _normal.z; float angleToNormal = FastMath.FastPseudoAcos(lightVectorToNormalDot) * Mathf.Rad2Deg; if (angleToNormal > _angleFadeMax) { // Skip shadows that fall with extreme angles _isVisible = false; return(RecalculateShadowResult.Skipped); } // Determine the forward direction of shadow base quad Vector3 forward; float dot = transformForward.x * actualLightVector.x + transformForward.y * actualLightVector.y + transformForward.z * actualLightVector.z; if (Mathf.Abs(dot) < 1f - Vector3.kEpsilon) { forward.x = transformForward.x - dot * actualLightVector.x; forward.y = transformForward.y - dot * actualLightVector.y; forward.z = transformForward.z - dot * actualLightVector.z; forward = forward.FastNormalized(); } else { // If the forward direction matches the light direction vector somehow Vector3 transformUp = transformRotation * kVector3Up; dot = transformUp.x * actualLightVector.x + transformUp.y * actualLightVector.y + transformUp.z * actualLightVector.z; forward.x = transformUp.x - dot * actualLightVector.x; forward.y = transformUp.y - dot * actualLightVector.y; forward.z = transformUp.z - dot * actualLightVector.z; forward = forward.FastNormalized(); } // Rotation of shadow base quad Vector3 actualLightVectorNegated; actualLightVectorNegated.x = -actualLightVector.x; actualLightVectorNegated.y = -actualLightVector.y; actualLightVectorNegated.z = -actualLightVector.z; Quaternion rotation = Quaternion.LookRotation(forward, actualLightVectorNegated); // Optimized version of // Vector3 right = rotation * Vector3.right; float num2 = rotation.y * 2f; float num3 = rotation.z * 2f; float num5 = rotation.y * num2; float num6 = rotation.z * num3; float num7 = rotation.x * num2; float num8 = rotation.x * num3; float num11 = rotation.w * num2; float num12 = rotation.w * num3; Vector3 right; right.x = 1f - (num5 + num6); right.y = num7 + num12; right.z = num8 - num11; // Base vertices calculation float aspectRatioRec = 1f / _aspectRatio; Vector3 tmp1; tmp1.x = forward.x * scaledShadowSize; tmp1.y = forward.y * scaledShadowSize; tmp1.z = forward.z * scaledShadowSize; Vector3 tmp2; tmp2.x = right.x * aspectRatioRec * scaledShadowSize; tmp2.y = right.y * aspectRatioRec * scaledShadowSize; tmp2.z = right.z * aspectRatioRec * scaledShadowSize; Vector3 diff; diff.x = tmp1.x - tmp2.x; diff.y = tmp1.y - tmp2.y; diff.z = tmp1.z - tmp2.z; Vector3 sum; sum.x = tmp1.x + tmp2.x; sum.y = tmp1.y + tmp2.y; sum.z = tmp1.z + tmp2.z; Vector3 baseVertex; baseVertex.x = transformPosition.x - sum.x; baseVertex.y = transformPosition.y - sum.y; baseVertex.z = transformPosition.z - sum.z; _baseVertices[0] = baseVertex; baseVertex.x = transformPosition.x + diff.x; baseVertex.y = transformPosition.y + diff.y; baseVertex.z = transformPosition.z + diff.z; _baseVertices[1] = baseVertex; baseVertex.x = transformPosition.x + sum.x; baseVertex.y = transformPosition.y + sum.y; baseVertex.z = transformPosition.z + sum.z; _baseVertices[2] = baseVertex; baseVertex.x = transformPosition.x - diff.x; baseVertex.y = transformPosition.y - diff.y; baseVertex.z = transformPosition.z - diff.z; _baseVertices[3] = baseVertex; // Calculate a plane from normal and position Vector3 offsetPoint; offsetPoint.x = hitPoint.x + _normal.x * _shadowOffset; offsetPoint.y = hitPoint.y + _normal.y * _shadowOffset; offsetPoint.z = hitPoint.z + _normal.z * _shadowOffset; FastMath.Plane shadowPlane = new FastMath.Plane(_normal, offsetPoint); float distanceToPlane; FastMath.Ray ray = new FastMath.Ray(); // Calculate the shadow vertices if (_isPerspectiveProjection && useLightSource) { ray.Direction.x = lightSourcePosition.x - _baseVertices[0].x; ray.Direction.y = lightSourcePosition.y - _baseVertices[0].y; ray.Direction.z = lightSourcePosition.z - _baseVertices[0].z; ray.Origin = _baseVertices[0]; shadowPlane.Raycast(ref ray, out distanceToPlane); _shadowVertices[0].x = ray.Origin.x + ray.Direction.x * distanceToPlane; _shadowVertices[0].y = ray.Origin.y + ray.Direction.y * distanceToPlane; _shadowVertices[0].z = ray.Origin.z + ray.Direction.z * distanceToPlane; ray.Direction.x = lightSourcePosition.x - _baseVertices[1].x; ray.Direction.y = lightSourcePosition.y - _baseVertices[1].y; ray.Direction.z = lightSourcePosition.z - _baseVertices[1].z; ray.Origin = _baseVertices[1]; shadowPlane.Raycast(ref ray, out distanceToPlane); _shadowVertices[1].x = ray.Origin.x + ray.Direction.x * distanceToPlane; _shadowVertices[1].y = ray.Origin.y + ray.Direction.y * distanceToPlane; _shadowVertices[1].z = ray.Origin.z + ray.Direction.z * distanceToPlane; ray.Direction.x = lightSourcePosition.x - _baseVertices[2].x; ray.Direction.y = lightSourcePosition.y - _baseVertices[2].y; ray.Direction.z = lightSourcePosition.z - _baseVertices[2].z; ray.Origin = _baseVertices[2]; shadowPlane.Raycast(ref ray, out distanceToPlane); _shadowVertices[2].x = ray.Origin.x + ray.Direction.x * distanceToPlane; _shadowVertices[2].y = ray.Origin.y + ray.Direction.y * distanceToPlane; _shadowVertices[2].z = ray.Origin.z + ray.Direction.z * distanceToPlane; ray.Direction.x = lightSourcePosition.x - _baseVertices[3].x; ray.Direction.y = lightSourcePosition.y - _baseVertices[3].y; ray.Direction.z = lightSourcePosition.z - _baseVertices[3].z; ray.Origin = _baseVertices[3]; shadowPlane.Raycast(ref ray, out distanceToPlane); _shadowVertices[3].x = ray.Origin.x + ray.Direction.x * distanceToPlane; _shadowVertices[3].y = ray.Origin.y + ray.Direction.y * distanceToPlane; _shadowVertices[3].z = ray.Origin.z + ray.Direction.z * distanceToPlane; } else { ray.Direction = actualLightVector; ray.Origin = _baseVertices[0]; shadowPlane.Raycast(ref ray, out distanceToPlane); _shadowVertices[0].x = ray.Origin.x + ray.Direction.x * distanceToPlane; _shadowVertices[0].y = ray.Origin.y + ray.Direction.y * distanceToPlane; _shadowVertices[0].z = ray.Origin.z + ray.Direction.z * distanceToPlane; ray.Origin = _baseVertices[1]; shadowPlane.Raycast(ref ray, out distanceToPlane); _shadowVertices[1].x = ray.Origin.x + ray.Direction.x * distanceToPlane; _shadowVertices[1].y = ray.Origin.y + ray.Direction.y * distanceToPlane; _shadowVertices[1].z = ray.Origin.z + ray.Direction.z * distanceToPlane; ray.Origin = _baseVertices[2]; shadowPlane.Raycast(ref ray, out distanceToPlane); _shadowVertices[2].x = ray.Origin.x + ray.Direction.x * distanceToPlane; _shadowVertices[2].y = ray.Origin.y + ray.Direction.y * distanceToPlane; _shadowVertices[2].z = ray.Origin.z + ray.Direction.z * distanceToPlane; ray.Origin = _baseVertices[3]; shadowPlane.Raycast(ref ray, out distanceToPlane); _shadowVertices[3].x = ray.Origin.x + ray.Direction.x * distanceToPlane; _shadowVertices[3].y = ray.Origin.y + ray.Direction.y * distanceToPlane; _shadowVertices[3].z = ray.Origin.z + ray.Direction.z * distanceToPlane; } // Calculate the shadow alpha float shadowAlpha = _initialAlpha; // Alpha base on distance to the surface if (distance > _fadeDistance) { shadowAlpha = shadowAlpha - (distance - _fadeDistance) / (_projectionDistance - _fadeDistance) * shadowAlpha; } // Alpha based on shadow fall angle if (angleToNormal > _angleFadeMin) { shadowAlpha = shadowAlpha - (angleToNormal - _angleFadeMin) / (_angleFadeMax - _angleFadeMin) * shadowAlpha; } // Convert float alpha to byte //_color.a = shadowAlpha; _color32.a = (byte)(shadowAlpha * 255f); _isVisible = true; } return(RecalculateShadowResult.Recalculated); }