NormalMapProcessor.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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 SpriteShaderPipeline
  16. {
  17. /// <summary>
  18. /// Custom content processor converts greyscale displacement bitmaps into
  19. /// normalmap 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. [ContentProcessor]
  25. public class NormalMapProcessor : ContentProcessor<TextureContent, TextureContent>
  26. {
  27. // Controls how extreme the output normalmap should be.
  28. const float bumpSize = 4f;
  29. /// <summary>
  30. /// Converts a greyscale displacement bitmap into normalmap format.
  31. /// </summary>
  32. public override TextureContent Process(TextureContent input,
  33. ContentProcessorContext context)
  34. {
  35. // Convert the input bitmap to Vector4 format, for ease of processing.
  36. input.ConvertBitmapType(typeof(PixelBitmapContent<Vector4>));
  37. PixelBitmapContent<Vector4> bitmap;
  38. bitmap = (PixelBitmapContent<Vector4>)input.Faces[0][0];
  39. // Calculate normalmap vectors.
  40. ConvertGreyToAlpha(bitmap);
  41. ConvertAlphaToNormals(bitmap);
  42. // Convert the result into NormalizedByte4 format.
  43. input.ConvertBitmapType(typeof(PixelBitmapContent<NormalizedByte4>));
  44. return input;
  45. }
  46. /// <summary>
  47. /// Copies greyscale color information into the alpha channel.
  48. /// </summary>
  49. static void ConvertGreyToAlpha(PixelBitmapContent<Vector4> bitmap)
  50. {
  51. for (int y = 0; y < bitmap.Height; y++)
  52. {
  53. for (int x = 0; x < bitmap.Width; x++)
  54. {
  55. Vector4 value = bitmap.GetPixel(x, y);
  56. // Copy a greyscale version of the RGB data into the alpha channel.
  57. float greyscale = (value.X + value.Y + value.Z) / 3;
  58. value.W = greyscale;
  59. bitmap.SetPixel(x, y, value);
  60. }
  61. }
  62. }
  63. /// <summary>
  64. /// Using height data stored in the alpha channel, computes normalmap
  65. /// vectors and stores them in the RGB portion of the bitmap.
  66. /// </summary>
  67. static void ConvertAlphaToNormals(PixelBitmapContent<Vector4> bitmap)
  68. {
  69. for (int y = 0; y < bitmap.Height; y++)
  70. {
  71. for (int x = 0; x < bitmap.Width; x++)
  72. {
  73. // Look up the heights to either side of this pixel.
  74. float left = GetHeight(bitmap, x - 1, y);
  75. float right = GetHeight(bitmap, x + 1, y);
  76. float top = GetHeight(bitmap, x, y - 1);
  77. float bottom = GetHeight(bitmap, x, y + 1);
  78. // Compute gradient vectors, then cross them to get the normal.
  79. Vector3 dx = new Vector3(1, 0, (right - left) * bumpSize);
  80. Vector3 dy = new Vector3(0, 1, (bottom - top) * bumpSize);
  81. Vector3 normal = Vector3.Cross(dx, dy);
  82. normal.Normalize();
  83. // Store the result.
  84. float alpha = GetHeight(bitmap, x, y);
  85. bitmap.SetPixel(x, y, new Vector4(normal, alpha));
  86. }
  87. }
  88. }
  89. /// <summary>
  90. /// Helper for looking up height values from the bitmap alpha channel,
  91. /// clamping if the specified position is off the edge of the bitmap.
  92. /// </summary>
  93. static float GetHeight(PixelBitmapContent<Vector4> bitmap, int x, int y)
  94. {
  95. if (x < 0)
  96. {
  97. x = 0;
  98. }
  99. else if (x >= bitmap.Width)
  100. {
  101. x = bitmap.Width - 1;
  102. }
  103. if (y < 0)
  104. {
  105. y = 0;
  106. }
  107. else if (y >= bitmap.Height)
  108. {
  109. y = bitmap.Height - 1;
  110. }
  111. return bitmap.GetPixel(x, y).W;
  112. }
  113. }
  114. }