NormalMapProcessor.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // NormalMapProcessor.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using Microsoft.Xna.Framework;
  11. using Microsoft.Xna.Framework.Content.Pipeline;
  12. using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
  13. using Microsoft.Xna.Framework.Graphics.PackedVector;
  14. #endregion
  15. namespace DistortionPipeline
  16. {
  17. /// <summary>
  18. /// Custom content processor converts greyscale displacement bitmaps into
  19. /// a normal-map format. In the source image, use black for pixels that are
  20. /// further away and white for ones that are bumped upward. The output
  21. /// texture contains three-component normal vectors. This processor works
  22. /// best if the source bitmap is slightly blurred.
  23. /// </summary>
  24. /// <remarks>
  25. /// This class was originally provided in the Sprite Effects sample.
  26. /// </remarks>
  27. [ContentProcessor]
  28. public class NormalMapProcessor : ContentProcessor<TextureContent, TextureContent>
  29. {
  30. // Controls how extreme the output normalmap should be.
  31. const float bumpSize = 4f;
  32. /// <summary>
  33. /// Converts a greyscale displacement bitmap into normalmap format.
  34. /// </summary>
  35. public override TextureContent Process(TextureContent input,
  36. ContentProcessorContext context)
  37. {
  38. // Convert the input bitmap to Vector4 format, for ease of processing.
  39. input.ConvertBitmapType(typeof(PixelBitmapContent<Vector4>));
  40. PixelBitmapContent<Vector4> bitmap;
  41. bitmap = (PixelBitmapContent<Vector4>)input.Faces[0][0];
  42. // Calculate normalmap vectors.
  43. ConvertGreyToAlpha(bitmap);
  44. ConvertAlphaToNormals(bitmap);
  45. // Convert the result into NormalizedByte4 format.
  46. input.ConvertBitmapType(typeof(PixelBitmapContent<NormalizedByte4>));
  47. return input;
  48. }
  49. /// <summary>
  50. /// Copies greyscale color information into the alpha channel.
  51. /// </summary>
  52. static void ConvertGreyToAlpha(PixelBitmapContent<Vector4> bitmap)
  53. {
  54. for (int y = 0; y < bitmap.Height; y++)
  55. {
  56. for (int x = 0; x < bitmap.Width; x++)
  57. {
  58. Vector4 value = bitmap.GetPixel(x, y);
  59. // Copy a greyscale version of the RGB data into the alpha channel.
  60. float greyscale = (value.X + value.Y + value.Z) / 3;
  61. value.W = greyscale;
  62. bitmap.SetPixel(x, y, value);
  63. }
  64. }
  65. }
  66. /// <summary>
  67. /// Using height data stored in the alpha channel, computes normalmap
  68. /// vectors and stores them in the RGB portion of the bitmap.
  69. /// </summary>
  70. static void ConvertAlphaToNormals(PixelBitmapContent<Vector4> bitmap)
  71. {
  72. for (int y = 0; y < bitmap.Height; y++)
  73. {
  74. for (int x = 0; x < bitmap.Width; x++)
  75. {
  76. // Look up the heights to either side of this pixel.
  77. float left = GetHeight(bitmap, x - 1, y);
  78. float right = GetHeight(bitmap, x + 1, y);
  79. float top = GetHeight(bitmap, x, y - 1);
  80. float bottom = GetHeight(bitmap, x, y + 1);
  81. // Compute gradient vectors, then cross them to get the normal.
  82. Vector3 dx = new Vector3(1, 0, (right - left) * bumpSize);
  83. Vector3 dy = new Vector3(0, 1, (bottom - top) * bumpSize);
  84. Vector3 normal = Vector3.Cross(dx, dy);
  85. normal.Normalize();
  86. // Store the result.
  87. float alpha = GetHeight(bitmap, x, y);
  88. bitmap.SetPixel(x, y, new Vector4(normal, alpha));
  89. }
  90. }
  91. }
  92. /// <summary>
  93. /// Helper for looking up height values from the bitmap alpha channel,
  94. /// clamping if the specified position is off the edge of the bitmap.
  95. /// </summary>
  96. static float GetHeight(PixelBitmapContent<Vector4> bitmap, int x, int y)
  97. {
  98. if (x < 0)
  99. {
  100. x = 0;
  101. }
  102. else if (x >= bitmap.Width)
  103. {
  104. x = bitmap.Width - 1;
  105. }
  106. if (y < 0)
  107. {
  108. y = 0;
  109. }
  110. else if (y >= bitmap.Height)
  111. {
  112. y = bitmap.Height - 1;
  113. }
  114. return bitmap.GetPixel(x, y).W;
  115. }
  116. }
  117. }