protected override void OnDrawBillboard(ref BillboardArgs b, PackedTexture texture, VertexPositionColorTexture[] vertices, int index) { #region ----- Billboarding ----- // The billboard orientation is defined by three vectors: normal (pointing to the camera), // up and right (both lying in the billboard plane). // normal and up are given. right is computed using the cross product up x normal. // normal and up should be perpendicular, but usually they are not. Therefore, one vector // must be corrected. For spherical billboards, the normal is fixed and the up vector // is corrected. For cylindrical billboards (= axial billboards), the up vector is fixed // and the b.Normal is corrected. // Normal if (b.Orientation.Normal == BillboardNormal.ViewpointOriented) { Vector3F normal = _cameraPose.Position - b.Position; if (normal.TryNormalize()) { b.Normal = normal; } } // Axis = up vector if (b.Orientation.IsAxisInViewSpace) { b.Axis = _cameraPose.ToWorldDirection(b.Axis); } if (1 - Vector3F.Dot(b.Normal, b.Axis) < Numeric.EpsilonF) { // Normal and axis are parallel. // --> Bend normal by adding a fraction of the camera down vector. b.Normal += _cameraDown * 0.001f; b.Normal.Normalize(); } // Compute right. //Vector3F right = Vector3F.Cross(b.Axis, b.Normal); // Inlined: Vector3F right; right.X = b.Axis.Y * b.Normal.Z - b.Axis.Z * b.Normal.Y; right.Y = b.Axis.Z * b.Normal.X - b.Axis.X * b.Normal.Z; right.Z = b.Axis.X * b.Normal.Y - b.Axis.Y * b.Normal.X; if (!right.TryNormalize()) { right = b.Normal.Orthonormal1; // Normal and axis are parallel --> Choose random perpendicular vector. } if (b.Orientation.IsAxisFixed) { // Make sure normal is perpendicular to right and up. //normal = Vector3F.Cross(right, b.Axis); // Inlined: b.Normal.X = right.Y * b.Axis.Z - right.Z * b.Axis.Y; b.Normal.Y = right.Z * b.Axis.X - right.X * b.Axis.Z; b.Normal.Z = right.X * b.Axis.Y - right.Y * b.Axis.X; // No need to normalize because right and up are normalized and perpendicular. } else { // Make sure axis is perpendicular to normal and right. //b.Axis = Vector3F.Cross(b.Normal, right); // Inlined: b.Axis.X = b.Normal.Y * right.Z - b.Normal.Z * right.Y; b.Axis.Y = b.Normal.Z * right.X - b.Normal.X * right.Z; b.Axis.Z = b.Normal.X * right.Y - b.Normal.Y * right.X; // No need to normalize because normal and right are normalized and perpendicular. } #endregion #region ----- Rotate up and right vectors ----- Vector3F upRotated; Vector3F rightRotated; if (b.Angle != 0.0f) { // Rotate up and right. // Here is the readable code. //Matrix33F rotation = Matrix33F.CreateRotation(b.Normal, b.Angle); //Vector3F upRotated = rotation * b.Axis; //Vector3F rightRotated = rotation * right; // Inlined code: float x = b.Normal.X; float y = b.Normal.Y; float z = b.Normal.Z; float x2 = x * x; float y2 = y * y; float z2 = z * z; float xy = x * y; float xz = x * z; float yz = y * z; float cos = (float)Math.Cos(b.Angle); float sin = (float)Math.Sin(b.Angle); float xsin = x * sin; float ysin = y * sin; float zsin = z * sin; float oneMinusCos = 1.0f - cos; float m00 = x2 + cos * (1.0f - x2); float m01 = xy * oneMinusCos - zsin; float m02 = xz * oneMinusCos + ysin; float m10 = xy * oneMinusCos + zsin; float m11 = y2 + cos * (1.0f - y2); float m12 = yz * oneMinusCos - xsin; float m20 = xz * oneMinusCos - ysin; float m21 = yz * oneMinusCos + xsin; float m22 = z2 + cos * (1.0f - z2); upRotated.X = m00 * b.Axis.X + m01 * b.Axis.Y + m02 * b.Axis.Z; upRotated.Y = m10 * b.Axis.X + m11 * b.Axis.Y + m12 * b.Axis.Z; upRotated.Z = m20 * b.Axis.X + m21 * b.Axis.Y + m22 * b.Axis.Z; rightRotated.X = m00 * right.X + m01 * right.Y + m02 * right.Z; rightRotated.Y = m10 * right.X + m11 * right.Y + m12 * right.Z; rightRotated.Z = m20 * right.X + m21 * right.Y + m22 * right.Z; } else { // Angle is 0 - no rotation. upRotated = b.Axis; rightRotated = right; } #endregion #region ----- Handle texture information and size ----- Vector2F texCoordTopLeft = texture.GetTextureCoordinates(Vector2F.Zero, b.AnimationTime); Vector2F texCoordBottomRight = texture.GetTextureCoordinates(Vector2F.One, b.AnimationTime); // Handle mirroring. if (b.Size.X < 0) { b.Size.X = -b.Size.X; MathHelper.Swap(ref texCoordTopLeft.X, ref texCoordBottomRight.X); } if (b.Size.Y < 0) { b.Size.Y = -b.Size.Y; MathHelper.Swap(ref texCoordTopLeft.Y, ref texCoordBottomRight.Y); } b.Size.X /= 2.0f; b.Size.Y /= 2.0f; // Offset from billboard center to right edge. Vector3F hOffset; hOffset.X = rightRotated.X * b.Size.X; hOffset.Y = rightRotated.Y * b.Size.X; hOffset.Z = rightRotated.Z * b.Size.X; // Offset from reference point to top edge. Vector3F vOffset; vOffset.X = upRotated.X * b.Size.Y; vOffset.Y = upRotated.Y * b.Size.Y; vOffset.Z = upRotated.Z * b.Size.Y; #endregion #region ----- Get Color ----- // Premultiply alpha. Vector4 color4 = new Vector4 { X = b.Color.X * b.Alpha, Y = b.Color.Y * b.Alpha, Z = b.Color.Z * b.Alpha, // Apply blend mode (0 = additive, 1 = alpha blend). W = b.Alpha * b.BlendMode }; var color = new Color(color4); #endregion #region ----- Initializes vertices in vertex array ----- // Bottom left vertex vertices[index].Position.X = b.Position.X - hOffset.X - vOffset.X; vertices[index].Position.Y = b.Position.Y - hOffset.Y - vOffset.Y; vertices[index].Position.Z = b.Position.Z - hOffset.Z - vOffset.Z; vertices[index].Color = color; vertices[index].TextureCoordinate.X = texCoordTopLeft.X; vertices[index].TextureCoordinate.Y = texCoordBottomRight.Y; index++; // Top left vertex vertices[index].Position.X = b.Position.X - hOffset.X + vOffset.X; vertices[index].Position.Y = b.Position.Y - hOffset.Y + vOffset.Y; vertices[index].Position.Z = b.Position.Z - hOffset.Z + vOffset.Z; vertices[index].Color = color; vertices[index].TextureCoordinate.X = texCoordTopLeft.X; vertices[index].TextureCoordinate.Y = texCoordTopLeft.Y; index++; // Top right vertex vertices[index].Position.X = b.Position.X + hOffset.X + vOffset.X; vertices[index].Position.Y = b.Position.Y + hOffset.Y + vOffset.Y; vertices[index].Position.Z = b.Position.Z + hOffset.Z + vOffset.Z; vertices[index].Color = color; vertices[index].TextureCoordinate.X = texCoordBottomRight.X; vertices[index].TextureCoordinate.Y = texCoordTopLeft.Y; index++; // Bottom right vertex vertices[index].Position.X = b.Position.X + hOffset.X - vOffset.X; vertices[index].Position.Y = b.Position.Y + hOffset.Y - vOffset.Y; vertices[index].Position.Z = b.Position.Z + hOffset.Z - vOffset.Z; vertices[index].Color = color; vertices[index].TextureCoordinate.X = texCoordBottomRight.X; vertices[index].TextureCoordinate.Y = texCoordBottomRight.Y; #endregion }
//-------------------------------------------------------------- #region Creation & Cleanup //-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="SsaoFilter"/> class. /// </summary> /// <param name="graphicsService">The graphics service.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="graphicsService"/> is <see langword="null"/>. /// </exception> public SsaoFilter(IGraphicsService graphicsService) : base(graphicsService) { _effect = GraphicsService.Content.Load <Effect>("DigitalRune/PostProcessing/SsaoFilter"); _farParameter = _effect.Parameters["Far"]; _radiusParameter = _effect.Parameters["Radius"]; _strengthParameter = _effect.Parameters["Strength"]; _maxDistancesParameter = _effect.Parameters["MaxDistances"]; _viewportSizeParameter = _effect.Parameters["ViewportSize"]; _sourceTextureParameter = _effect.Parameters["SourceTexture"]; _gBuffer0Parameter = _effect.Parameters["GBuffer0"]; _occlusionTextureParameter = _effect.Parameters["OcclusionTexture"]; _createLinesAPass = _effect.CurrentTechnique.Passes["CreateLinesA"]; _createLinesBPass = _effect.CurrentTechnique.Passes["CreateLinesB"]; _blurHorizontalPass = _effect.CurrentTechnique.Passes["BlurHorizontal"]; _blurVerticalPass = _effect.CurrentTechnique.Passes["BlurVertical"]; _combinePass = _effect.CurrentTechnique.Passes["Combine"]; _copyPass = _effect.CurrentTechnique.Passes["Copy"]; Radii = new Vector2F(0.01f, 0.02f); MaxDistances = new Vector2F(0.5f, 1.0f); Strength = 1f; NumberOfBlurPasses = 1; DownsampleFactor = 2; Quality = 2; Scale = new Vector2F(0.5f, 2f); CombineWithSource = true; _blur = new Blur(graphicsService); _blur.InitializeGaussianBlur(7, 7 / 3, true); _copyFilter = PostProcessHelper.GetCopyFilter(graphicsService); _downsampleFilter = PostProcessHelper.GetDownsampleFilter(graphicsService); Random random = new Random(123456); Vector3[] vectors = new Vector3[9]; // 16 random vectors for Crytek-style point samples. //for (int i = 0; i < vectors.Length; i++) // vectors[i] = (Vector3)random.NextQuaternionF().Rotate(Vector3F.One).Normalized; // //* random.NextFloat(0.5f, 1) // Note: StarCraft 2 uses varying length to vary the sample offset length. // We create rotated random vectors with uniform distribution in 360°. Each random vector // is further rotated with small random angle. float jitterAngle = ConstantsF.TwoPi / vectors.Length / 4; for (int i = 0; i < vectors.Length; i++) { vectors[i] = (Vector3)(Matrix33F.CreateRotationZ(ConstantsF.TwoPi * i / vectors.Length + random.NextFloat(-jitterAngle, jitterAngle)) * new Vector3F(1, 0, 0)).Normalized; } // Permute randomVectors. for (int i = 0; i < vectors.Length; i++) { MathHelper.Swap(ref vectors[i], ref vectors[random.Next(i, vectors.Length - 1)]); } // Scale random vectors. for (int i = 0; i < vectors.Length; i++) { vectors[i].Z = random.NextFloat(Scale.X, Scale.Y); } _effect.Parameters["RandomVectors"].SetValue(vectors); }