/// <summary> /// Computes the angular attenuation (spotlight falloff) for a given angle. /// </summary> /// <param name="angle">The angle relative to the main light direction in radians.</param> /// <param name="falloffAngle">The falloff angle.</param> /// <param name="cutoffAngle">The cutoff angle.</param> /// <returns> /// The angular attenuation of the light intensity. (1 when <paramref name="angle"/> is less /// than or equal to <paramref name="falloffAngle"/>. 0 when <paramref name="angle"/> is /// greater than or equal to <paramref name="cutoffAngle"/>.) /// </returns> /// <remarks> /// <para> /// The falloff between <paramref name="falloffAngle"/> and <paramref name="cutoffAngle"/> is /// computed using a smoothstep function (see /// <see cref="InterpolationHelper.HermiteSmoothStep(float)"/>). /// </para> /// <para> /// <i>angularAttenuation</i> = smoothstep((cos(<i>angle</i>) - cos(<i>cutoffAngle</i>)) / /// (cos(<i>falloffAngle</i>) - cos(<i>cutoffAngle</i>))) /// </para> /// </remarks> public static float GetAngularAttenuation(float angle, float falloffAngle, float cutoffAngle) { if (angle < 0) { angle = -angle; } if (angle <= falloffAngle) { return(1.0f); } if (angle >= cutoffAngle) { return(0.0f); } float cosOuterCone = (float)Math.Cos(cutoffAngle); float cosInnerCone = (float)Math.Cos(falloffAngle); float cosAngle = (float)Math.Cos(angle); float cosDiff = cosInnerCone - cosOuterCone; if (Numeric.IsZero(cosDiff)) { return(0.0f); } float x = (cosAngle - cosOuterCone) / cosDiff; return(InterpolationHelper.HermiteSmoothStep(x)); }
public void HermiteSmoothStepF() { Assert.IsTrue(Numeric.AreEqual(0, InterpolationHelper.HermiteSmoothStep(-1.0))); Assert.IsTrue(Numeric.AreEqual(0, InterpolationHelper.HermiteSmoothStep(0.0))); Assert.IsTrue(Numeric.AreEqual(0.5, InterpolationHelper.HermiteSmoothStep(0.5))); Assert.IsTrue(Numeric.AreEqual(1, InterpolationHelper.HermiteSmoothStep(1.0))); Assert.IsTrue(Numeric.AreEqual(1, InterpolationHelper.HermiteSmoothStep(2.0))); Assert.IsTrue(Numeric.AreEqual(1 - InterpolationHelper.HermiteSmoothStep(1 - 0.3), InterpolationHelper.HermiteSmoothStep(0.3))); Assert.Greater(InterpolationHelper.HermiteSmoothStep(1 - 0.3), InterpolationHelper.HermiteSmoothStep(0.3)); }
// Returns the flow vector for a given position. // x and y are in the range [0, 1]. private static void GetFlow(Vector2F position, out Vector2F direction, out float speed) { // Create a circular movement around (0.5, 0.5). // Vector from center to position is: var radius = position - new Vector2F(0.5f, 0.5f); // The flow direction is orthogonal to the radius vector. direction = new Vector2F(radius.Y, -radius.X); direction.TryNormalize(); // The speed is max in the center and is 0 at the texture border. speed = 1; if (!Numeric.IsZero(radius.Length)) { speed = 1 - InterpolationHelper.HermiteSmoothStep(MathHelper.Clamp((radius.Length - 0.1f) / 0.4f, 0, 1)); } }
/// <summary> /// Called to compute the fog intensity for <see cref="GetIntensity"/>. /// </summary> /// <param name="fogNode">The fog node. (Is never <see langword="null"/>.)</param> /// <param name="cameraNode">The camera node. (Is never <see langword="null"/>.)</param> /// <param name="targetPosition">The target position.</param> /// <returns>The fog intensity (0 = no fog; 1 = full fog, nothing else visible).</returns> /*protected virtual*/ private float OnGetIntensity(FogNode fogNode, CameraNode cameraNode, Vector3 targetPosition) { // These computations are the same as in FogRenderer and the Fog shader files. if (Numeric.IsZero(Density)) { return(0); } Vector3 cameraToPosition = targetPosition - cameraNode.PoseWorld.Position; float distance = cameraToPosition.Length; // The distance traveled inside the fog. Vector3 cameraToPositionDirection = cameraToPosition / distance; // Compute a value that is 0 at Start and 1 at End. float ramp = (distance - Start) / (End - Start); // Smoothstep distance fog float smoothRamp = InterpolationHelper.HermiteSmoothStep(ramp); // Exponential Fog float referenceHeight = cameraNode.PoseWorld.Position.Y - fogNode.PoseWorld.Position.Y + cameraToPositionDirection.Y * Start; float distanceInFog = distance - Start; var fogDirection = cameraToPositionDirection; if (HeightFalloff * fogDirection.Y < 0) { referenceHeight += fogDirection.Y * distanceInFog; fogDirection = -fogDirection; } float referenceDensity = Density * (float)Math.Pow(2, -HeightFalloff * referenceHeight); float opticalLength = GetOpticalLengthInHeightFog(distanceInFog, referenceDensity, fogDirection * distanceInFog, HeightFalloff); float heightFog = (1 - (float)Math.Pow(2, -opticalLength * 1)); // Get alpha from BottomColor and TopColor. // (Note: We have to avoid division by zero.) float height = targetPosition.Y - fogNode.PoseWorld.Position.Y; float p = MathHelper.Clamp((height - Height0) / Math.Max(Height1 - Height0, 0.0001f), 0, 1); float alpha = InterpolationHelper.Lerp(Color0.W, Color1.W, p); return(smoothRamp * heightFog * alpha); }
private static void ClampHeightsToLineSegments(HeightField terrain, Aabb aabb, List <Vector4F> segments, float padding) { // TODO: Optimize this (see software rasterizers). float originX = terrain.OriginX; float originZ = terrain.OriginZ; int numberOfSamplesX = terrain.NumberOfSamplesX; int numberOfSamplesZ = terrain.NumberOfSamplesZ; int numberOfCellsX = numberOfSamplesX - 1; int numberOfCellsZ = numberOfSamplesZ - 1; float widthX = terrain.WidthX; float cellSizeX = widthX / numberOfCellsX; float widthZ = terrain.WidthZ; float cellSizeZ = widthZ / numberOfCellsZ; float[] heights = terrain.Samples; // Get min and max indices (inclusive). float minX = aabb.Minimum.X; float maxX = aabb.Maximum.X; float minZ = aabb.Minimum.Z; float maxZ = aabb.Maximum.Z; // Get min and max indices (inclusive). int indexXMin = Math.Max(0, (int)Math.Floor((minX - originX) / cellSizeX)); int indexZMin = Math.Max(0, (int)Math.Floor((minZ - originZ) / cellSizeZ)); int indexXMax = Math.Min(numberOfSamplesX - 1, (int)Math.Ceiling((maxX - originX) / cellSizeX)); int indexZMax = Math.Min(numberOfSamplesZ - 1, (int)Math.Ceiling((maxZ - originZ) / cellSizeZ)); Parallel.For(indexZMin, indexZMax + 1, indexZ => //for (int indexZ = indexZMin; indexZ <= indexZMax; indexZ++) { for (int indexX = indexXMin; indexX <= indexXMax; indexX++) { Vector3F terrainPointFlat = new Vector3F(originX + cellSizeX * indexX, 0, originZ + cellSizeZ * indexZ); float bestSegmentInfluence = 0; float bestSegmentHeight = 0; for (int segmentIndex = 0; segmentIndex < segments.Count / 2; segmentIndex++) { var segmentStartFlat = new Vector3F(segments[segmentIndex * 2].X, 0, segments[segmentIndex * 2].Z); var segmentEndFlat = new Vector3F(segments[segmentIndex * 2 + 1].X, 0, segments[segmentIndex * 2 + 1].Z); var segment = new LineSegment(segmentStartFlat, segmentEndFlat); float parameter; GetLineParameter(ref segment, ref terrainPointFlat, out parameter); Vector4F closestPoint = segments[segmentIndex * 2] + parameter * (segments[segmentIndex * 2 + 1] - segments[segmentIndex * 2]); Vector3F closestPointFlat = new Vector3F(closestPoint.X, 0, closestPoint.Z); float distance = (closestPointFlat - terrainPointFlat).Length - padding; float influence = MathHelper.Clamp(1 - distance / (closestPoint.W - padding), 0, 1); if (influence > bestSegmentInfluence) { bestSegmentInfluence = influence; bestSegmentHeight = closestPoint.Y; } } if (bestSegmentInfluence > 0) { heights[indexZ * numberOfSamplesX + indexX] = InterpolationHelper.Lerp( heights[indexZ * numberOfSamplesX + indexX], bestSegmentHeight, InterpolationHelper.HermiteSmoothStep(bestSegmentInfluence)); } } }); }