PerPixelLighting.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // PerPixelLighting.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 System;
  11. using Microsoft.Xna.Framework;
  12. using Microsoft.Xna.Framework.Content;
  13. using Microsoft.Xna.Framework.Graphics;
  14. using Microsoft.Xna.Framework.Input;
  15. #endregion
  16. namespace PerPixelLightingSample
  17. {
  18. /// <summary>
  19. /// The central class for the sample Game.
  20. /// </summary>
  21. public class PerPixelLighting : Microsoft.Xna.Framework.Game
  22. {
  23. #region Sample Fields
  24. private GraphicsDeviceManager graphics;
  25. private SampleArcBallCamera camera;
  26. private Vector2 safeBounds;
  27. private Vector2 debugTextHeight;
  28. private Model[] sampleMeshes;
  29. private SampleGrid grid;
  30. private int activeMesh, activeEffect, activeTechnique, activeCombination;
  31. private int[,] effectTechniqueCombinations =
  32. {
  33. {0, 0}, {1, 0}, {0, 1}, {1, 1}, {1, 2}
  34. };
  35. private int effectTechniqueCombinationCount = 5;
  36. private SpriteBatch spriteBatch;
  37. private SpriteFont debugTextFont;
  38. private GamePadState lastGpState;
  39. private KeyboardState lastKbState;
  40. #endregion
  41. #region Specular Constant Fields
  42. private const float specularPowerMinimum = 0.5f;
  43. private const float specularPowerMaximum = 128f;
  44. private const float specularIntensityMinimum = 0.01f;
  45. private const float specularIntensityMaximum = 10f;
  46. #endregion
  47. /// <summary>
  48. /// Example 1.1: Effect objects used for this example
  49. /// </summary>
  50. #region Effect Fields
  51. private Effect[] effects;
  52. private EffectParameter[] worldParameter = new EffectParameter[2];
  53. private EffectParameter[] viewParameter = new EffectParameter[2];
  54. private EffectParameter[] projectionParameter = new EffectParameter[2];
  55. private EffectParameter[] cameraPositionParameter = new EffectParameter[2];
  56. private EffectParameter[] specularPowerParameter = new EffectParameter[2];
  57. private EffectParameter[] specularIntensityParameter = new EffectParameter[2];
  58. #endregion
  59. /// <summary>
  60. /// Example 1.2: Data fields corresponding to the effect paramters
  61. /// </summary>
  62. #region Uniform Data Fields
  63. private Matrix world;
  64. private float specularPower, specularIntensity;
  65. #endregion
  66. #region Initialization and Cleanup
  67. public PerPixelLighting()
  68. {
  69. graphics = new GraphicsDeviceManager(this);
  70. Content.RootDirectory = "Content";
  71. }
  72. /// <summary>
  73. /// Initialize the sample.
  74. /// </summary>
  75. protected override void Initialize()
  76. {
  77. // create a default world and matrix
  78. world = Matrix.Identity;
  79. // create the mesh array
  80. sampleMeshes = new Model[5];
  81. // Set up the reference grid
  82. grid = new SampleGrid();
  83. grid.GridColor = Color.LimeGreen;
  84. grid.GridScale = 1.0f;
  85. grid.GridSize = 32;
  86. // Set the grid to draw on the x/z plane around the origin
  87. grid.WorldMatrix = Matrix.Identity;
  88. // set up the sample camera
  89. camera = new SampleArcBallCamera(SampleArcBallCameraMode.RollConstrained);
  90. camera.Distance = 3;
  91. // orbit the camera so we're looking down the z=-1 axis,
  92. // at the "front" of the object
  93. camera.OrbitRight(MathHelper.Pi);
  94. // orbit up a bit for perspective
  95. camera.OrbitUp(.2f);
  96. // set the initial effect, technique, and mesh
  97. activeMesh = 1;
  98. activeCombination = 0;
  99. activeEffect = effectTechniqueCombinations[activeCombination, 0];
  100. activeTechnique = effectTechniqueCombinations[activeCombination, 1];
  101. // set the initial specular values
  102. specularPower = 16;
  103. specularIntensity = 1;
  104. base.Initialize();
  105. }
  106. /// <summary>
  107. /// Load the graphics content.
  108. /// </summary>
  109. protected override void LoadContent()
  110. {
  111. // Set up the reference grid and sample camera
  112. grid.LoadGraphicsContent(graphics.GraphicsDevice);
  113. // create the spritebatch for debug text
  114. spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
  115. // load meshes
  116. sampleMeshes[0] = Content.Load<Model>("Cube");
  117. sampleMeshes[1] = Content.Load<Model>("SphereHighPoly");
  118. sampleMeshes[2] = Content.Load<Model>("SphereLowPoly");
  119. sampleMeshes[3] = Content.Load<Model>("Cylinder");
  120. sampleMeshes[4] = Content.Load<Model>("Cone");
  121. // load the sprite font for debug text
  122. debugTextFont = Content.Load<SpriteFont>("DebugText");
  123. debugTextHeight = new Vector2(0, debugTextFont.LineSpacing + 5);
  124. // load the effects
  125. effects = new Effect[2];
  126. effects[0] = Content.Load<Effect>("VertexLighting");
  127. effects[1] = Content.Load<Effect>("PerPixelLighting");
  128. for (int i = 0; i < 2; i++)
  129. {
  130. // cache the effect parameters
  131. worldParameter[i] = effects[i].Parameters["world"];
  132. viewParameter[i] = effects[i].Parameters["view"];
  133. projectionParameter[i] = effects[i].Parameters["projection"];
  134. cameraPositionParameter[i] = effects[i].Parameters["cameraPosition"];
  135. specularPowerParameter[i] = effects[i].Parameters["specularPower"];
  136. specularIntensityParameter[i] = effects[i].Parameters["specularIntensity"];
  137. cameraPositionParameter[i] = effects[i].Parameters["cameraPosition"];
  138. //
  139. // set up some basic effect parameters that do not change during the
  140. // course of execution
  141. //
  142. // set the light colors
  143. effects[i].Parameters["ambientLightColor"].SetValue(
  144. Color.DarkSlateGray.ToVector4());
  145. effects[i].Parameters["diffuseLightColor"].SetValue(
  146. Color.CornflowerBlue.ToVector4());
  147. effects[i].Parameters["specularLightColor"].SetValue(
  148. Color.White.ToVector4());
  149. // Set the light position to a fixed location.
  150. // This will place the light source behind, to the right, and above the
  151. // initial camera position.
  152. effects[i].Parameters["lightPosition"].SetValue(
  153. new Vector3(30f, 30f, 30f));
  154. }
  155. // Recalculate the projection properties on every LoadGraphicsContent call.
  156. // That way, if the window gets resized, then the perspective matrix will be
  157. // updated accordingly
  158. float aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
  159. (float)graphics.GraphicsDevice.Viewport.Height;
  160. float fieldOfView = aspectRatio * MathHelper.PiOver4 * 3f / 4f;
  161. grid.ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(
  162. fieldOfView, aspectRatio, .1f, 1000f);
  163. // calculate the safe left and top edges of the screen
  164. safeBounds = new Vector2(
  165. (float)graphics.GraphicsDevice.Viewport.X +
  166. (float)graphics.GraphicsDevice.Viewport.Width * 0.1f,
  167. (float)graphics.GraphicsDevice.Viewport.Y +
  168. (float)graphics.GraphicsDevice.Viewport.Height * 0.1f
  169. );
  170. }
  171. #endregion
  172. #region Update and Render
  173. /// <summary>
  174. /// Update the game world.
  175. /// </summary>
  176. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  177. protected override void Update(GameTime gameTime)
  178. {
  179. GamePadState gpState = GamePad.GetState(PlayerIndex.One);
  180. KeyboardState kbState = Keyboard.GetState();
  181. // Check for exit
  182. if ((gpState.Buttons.Back == ButtonState.Pressed) ||
  183. kbState.IsKeyDown(Keys.Escape))
  184. {
  185. Exit();
  186. }
  187. // Handle inputs for the sample camera
  188. camera.HandleDefaultGamepadControls(gpState, gameTime);
  189. camera.HandleDefaultKeyboardControls(kbState, gameTime);
  190. // Handle inputs specific to this sample
  191. HandleInput(gameTime, gpState, kbState);
  192. // The built-in camera class provides the view matrix
  193. grid.ViewMatrix = camera.ViewMatrix;
  194. // The camera position should also be updated for the
  195. // Phong specular component to be meaningful
  196. cameraPositionParameter[activeEffect].SetValue(camera.Position);
  197. // replace the "last" gamepad and keyboard states
  198. lastGpState = gpState;
  199. lastKbState = kbState;
  200. base.Update(gameTime);
  201. }
  202. private void HandleInput(GameTime gameTime, GamePadState gpState,
  203. KeyboardState kbState)
  204. {
  205. float elapsedTime = (float) gameTime.ElapsedGameTime.TotalSeconds;
  206. //Handle input for selecting meshes
  207. if (((gpState.Buttons.X == ButtonState.Pressed) &&
  208. (lastGpState.Buttons.X == ButtonState.Released)) ||
  209. (kbState.IsKeyDown(Keys.Tab) && lastKbState.IsKeyUp(Keys.Tab)))
  210. {
  211. //switch the active mesh
  212. activeMesh = (activeMesh + 1) % sampleMeshes.Length;
  213. }
  214. //Handle input for selecting the active effect
  215. if (((gpState.Buttons.Y == ButtonState.Pressed) &&
  216. (lastGpState.Buttons.Y == ButtonState.Released)) ||
  217. (kbState.IsKeyDown(Keys.Space) && lastKbState.IsKeyUp(Keys.Space)))
  218. {
  219. activeCombination = (activeCombination + 1) %
  220. effectTechniqueCombinationCount;
  221. activeEffect = effectTechniqueCombinations[activeCombination, 0];
  222. activeTechnique = effectTechniqueCombinations[activeCombination, 1];
  223. }
  224. //handle mesh rotation inputs
  225. float dx =
  226. SampleArcBallCamera.ReadKeyboardAxis(kbState, Keys.Left, Keys.Right) +
  227. gpState.ThumbSticks.Left.X;
  228. float dy =
  229. SampleArcBallCamera.ReadKeyboardAxis(kbState, Keys.Down, Keys.Up) +
  230. gpState.ThumbSticks.Left.Y;
  231. //apply mesh rotation to world matrix
  232. if (dx != 0)
  233. {
  234. world *= Matrix.CreateFromAxisAngle(camera.Up, elapsedTime * dx);
  235. }
  236. if (dy != 0)
  237. {
  238. world *= Matrix.CreateFromAxisAngle(camera.Right, elapsedTime * -dy);
  239. }
  240. //handle specular power and intensity inputs
  241. float dPower = SampleArcBallCamera.ReadKeyboardAxis(kbState,
  242. Keys.Multiply, Keys.Divide);
  243. if (gpState.DPad.Right == ButtonState.Pressed)
  244. {
  245. dPower = 1;
  246. }
  247. if (gpState.DPad.Left == ButtonState.Pressed)
  248. {
  249. dPower = -1;
  250. }
  251. float dIntensity = SampleArcBallCamera.ReadKeyboardAxis(kbState,
  252. Keys.Add, Keys.Subtract);
  253. if (gpState.DPad.Up == ButtonState.Pressed)
  254. {
  255. dIntensity = 1;
  256. }
  257. if (gpState.DPad.Down == ButtonState.Pressed)
  258. {
  259. dIntensity = -1;
  260. }
  261. if (dPower != 0)
  262. {
  263. specularPower *= 1 + (elapsedTime * dPower);
  264. specularPower = MathHelper.Clamp(specularPower,
  265. specularPowerMinimum, specularPowerMaximum);
  266. }
  267. if (dIntensity != 0)
  268. {
  269. specularIntensity *= 1 + (elapsedTime * dIntensity);
  270. specularIntensity = MathHelper.Clamp(specularIntensity,
  271. specularIntensityMinimum, specularIntensityMaximum);
  272. }
  273. }
  274. /// <summary>
  275. /// Example 1.4
  276. ///
  277. /// The effect parameters set in this function
  278. /// are shared between all of the rendered elements in the scene.
  279. /// </summary>
  280. private void SetSharedEffectParameters()
  281. {
  282. worldParameter[activeEffect].SetValue(world);
  283. viewParameter[activeEffect].SetValue(grid.ViewMatrix);
  284. projectionParameter[activeEffect].SetValue(grid.ProjectionMatrix);
  285. specularPowerParameter[activeEffect].SetValue(specularPower);
  286. specularIntensityParameter[activeEffect].SetValue(specularIntensity);
  287. }
  288. /// <summary>
  289. /// Draw the current scene.
  290. /// </summary>
  291. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  292. protected override void Draw(GameTime gameTime)
  293. {
  294. graphics.GraphicsDevice.Clear(Color.Black);
  295. // the SpriteBatch added below to draw the debug text is changing some
  296. // needed render states, so they are reset here.
  297. graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
  298. // draw the reference grid so it's easier to get our bearings
  299. grid.Draw();
  300. // always set the shared effects parameters
  301. SetSharedEffectParameters();
  302. // draw the mesh itself
  303. DrawSampleMesh(sampleMeshes[activeMesh]);
  304. // draw the technique name and specular settings
  305. spriteBatch.Begin();
  306. spriteBatch.DrawString(debugTextFont,
  307. effects[activeEffect].CurrentTechnique.Name,
  308. safeBounds, Color.White);
  309. spriteBatch.DrawString(debugTextFont, "Specular Power: " +
  310. specularPower.ToString("0.00"),
  311. safeBounds + (1f * debugTextHeight), Color.White);
  312. spriteBatch.DrawString(debugTextFont, "Specular Intensity: " +
  313. specularIntensity.ToString("0.00"),
  314. safeBounds + (2f * debugTextHeight), Color.White);
  315. spriteBatch.End();
  316. base.Draw(gameTime);
  317. }
  318. /// <summary>
  319. /// Example 1.6
  320. ///
  321. /// Draws a sample mesh using a single effect with a single technique.
  322. /// This pattern is very common in simple effect usage.
  323. /// </summary>
  324. /// <param name="sampleMesh"></param>
  325. public void DrawSampleMesh(Model sampleMesh)
  326. {
  327. if (sampleMesh == null)
  328. return;
  329. // our sample meshes only contain a single part, so we don't need to bother
  330. // looping over the ModelMesh and ModelMeshPart collections. If the meshes
  331. // were more complex, we would repeat all the following code for each part
  332. ModelMesh mesh = sampleMesh.Meshes[0];
  333. ModelMeshPart meshPart = mesh.MeshParts[0];
  334. // set the vertex source to the mesh's vertex buffer
  335. graphics.GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer, meshPart.VertexOffset);
  336. // set the current index buffer to the sample mesh's index buffer
  337. graphics.GraphicsDevice.Indices = meshPart.IndexBuffer;
  338. // determine the current effect and technique
  339. effects[activeEffect].CurrentTechnique =
  340. effects[activeEffect].Techniques[activeTechnique];
  341. // now we loop through the passes in the teqnique, drawing each
  342. // one in order
  343. int passCount = effects[activeEffect].CurrentTechnique.Passes.Count;
  344. for (int i = 0; i < passCount; i++)
  345. {
  346. // EffectPass.Apply will update the device to
  347. // begin using the state information defined in the current pass
  348. effects[activeEffect].CurrentTechnique.Passes[i].Apply();
  349. // sampleMesh contains all of the information required to draw
  350. // the current mesh
  351. graphics.GraphicsDevice.DrawIndexedPrimitives(
  352. PrimitiveType.TriangleList, 0, 0,
  353. meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);
  354. }
  355. }
  356. #endregion
  357. #region Entry Point
  358. /// <summary>
  359. /// The main entry point for the application.
  360. /// </summary>
  361. static void Main()
  362. {
  363. using (PerPixelLighting game = new PerPixelLighting())
  364. {
  365. game.Run();
  366. }
  367. }
  368. #endregion
  369. }
  370. }