NormalMapProcessor.cs 4.6 KB

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