Main.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. // Framework includes
  2. #include "BsApplication.h"
  3. #include "Resources/BsResources.h"
  4. #include "Resources/BsBuiltinResources.h"
  5. #include "Material/BsMaterial.h"
  6. #include "Components/BsCCamera.h"
  7. #include "Components/BsCRenderable.h"
  8. #include "Components/BsCSkybox.h"
  9. #include "Components/BsCLight.h"
  10. #include "GUI/BsCGUIWidget.h"
  11. #include "GUI/BsGUIPanel.h"
  12. #include "GUI/BsGUILayoutY.h"
  13. #include "GUI/BsGUILabel.h"
  14. #include "RenderAPI/BsRenderAPI.h"
  15. #include "RenderAPI/BsRenderWindow.h"
  16. #include "Scene/BsSceneObject.h"
  17. #include "Renderer/BsRenderer.h"
  18. #include "Material/BsShader.h"
  19. // Example includes
  20. #include "BsCameraFlyer.h"
  21. #include "BsObjectRotator.h"
  22. #include "BsExampleFramework.h"
  23. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  24. // This example renders an object using a variety of custom materials, showing you how you can customize the rendering of
  25. // your objects if the built-in materials are not adequate. The example is structuraly very similar to the
  26. // PhysicallyBasedShading example, with the addition of custom materials. The most important part of this example are in
  27. // fact the shaders that it uses, so make sure to also study the BSL code of the shaders we import below.
  28. //
  29. // The example first loads necessary resources, including a mesh and textures to use for rendering. Then it imports a set
  30. // of custom shaders and creates a set of materials based on those shaders. It then proceeds to register the relevant keys
  31. // used for controling the camera and the rendered object, as well as a key to switch between different materials. It then
  32. // sets up the 3D scene using the mesh, textures, and the initial material, as well as a camera, along with CameraFlyer and
  33. // ObjectRotator components that allow the user to fly around the scene and rotate the 3D model. Finally it hooks up a
  34. // callback that switches between the materials when the user presses the relevant key, and adds a bit of GUI to let the
  35. // user know which key to press.
  36. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  37. namespace bs
  38. {
  39. UINT32 windowResWidth = 1280;
  40. UINT32 windowResHeight = 720;
  41. /** Container for all resources used by the example. */
  42. struct Assets
  43. {
  44. HMesh sphere;
  45. HTexture exampleAlbedoTex;
  46. HTexture exampleNormalsTex;
  47. HTexture exampleRoughnessTex;
  48. HTexture exampleMetalnessTex;
  49. HTexture skyTex;
  50. HMaterial standardMaterial;
  51. HMaterial vertexMaterial;
  52. HMaterial deferredSurfaceMaterial;
  53. HMaterial forwardMaterial;
  54. HShader deferredLightingShader;
  55. };
  56. Assets gAssets;
  57. /** Helper method that creates a material from the provided shader, and assigns the relevant PBR textures. */
  58. HMaterial createPBRMaterial(const HShader& shader, const Assets& assets)
  59. {
  60. HMaterial material = Material::create(shader);
  61. material->setTexture("gAlbedoTex", assets.exampleAlbedoTex);
  62. material->setTexture("gNormalTex", assets.exampleNormalsTex);
  63. material->setTexture("gRoughnessTex", assets.exampleRoughnessTex);
  64. material->setTexture("gMetalnessTex", assets.exampleMetalnessTex);
  65. return material;
  66. }
  67. /** Load the resources we'll be using throughout the example. */
  68. Assets loadAssets()
  69. {
  70. Assets assets;
  71. // Load a 3D model
  72. assets.sphere = ExampleFramework::loadMesh(ExampleMesh::Pistol, 10.0f);
  73. // Load PBR textures for the 3D model
  74. assets.exampleAlbedoTex = ExampleFramework::loadTexture(ExampleTexture::PistolAlbedo);
  75. assets.exampleNormalsTex = ExampleFramework::loadTexture(ExampleTexture::PistolNormal, false);
  76. assets.exampleRoughnessTex = ExampleFramework::loadTexture(ExampleTexture::PistolRoughness, false);
  77. assets.exampleMetalnessTex = ExampleFramework::loadTexture(ExampleTexture::PistolMetalness, false);
  78. // Create a set of materials we'll be using for rendering the object
  79. //// Create a standard PBR material
  80. HShader standardShader = gBuiltinResources().getBuiltinShader(BuiltinShader::Standard);
  81. assets.standardMaterial = createPBRMaterial(standardShader, assets);
  82. //// Create a material that overrides the vertex transform of the rendered model. This creates a wobble in the model
  83. //// geometry, but doesn't otherwise change the lighting properties (i.e. it still uses the PBR lighting model).
  84. HShader vertexShader = ExampleFramework::loadShader(ExampleShader::CustomVertex);
  85. assets.vertexMaterial = createPBRMaterial(vertexShader, assets);
  86. //// Create a material that overrides the surface data that gets used by the lighting evaluation. The material
  87. //// ignores the albedo texture provided, and instead uses a noise function to generate the albedo values.
  88. HShader deferredSurfaceShader = ExampleFramework::loadShader(ExampleShader::CustomDeferredSurface);
  89. assets.deferredSurfaceMaterial = createPBRMaterial(deferredSurfaceShader, assets);
  90. //// Create a material that overrides the lighting calculation by implementing a custom BRDF function, in this case
  91. //// using a basic Lambert BRDF. Note that lighting calculations for the deferred pipeline are done globally, so
  92. //// this material is created and used differently than others in this example. Instead of being assigned to
  93. //// Renderable it is instead applied globally and will affect all objects using the deferred pipeline.
  94. assets.deferredLightingShader = ExampleFramework::loadShader(ExampleShader::CustomDeferredLighting);
  95. //// Creates a material that uses the forward rendering pipeline, while all previous materials have used the
  96. //// deferred rendering pipeline. Forward rendering is required when the shader is used for rendering transparent
  97. //// geometry, as this is not supported by the deferred pipeline. Forward rendering shader contains both the surface
  98. //// and lighting portions in a single shader (unlike with deferred). This custom shader overrides both, using a
  99. //// noise function for generating the surface albedo, and overriding the PBR BRDF with a basic Lambert BRDF.
  100. HShader forwardSurfaceAndLighting = ExampleFramework::loadShader(ExampleShader::CustomForward);
  101. assets.forwardMaterial = createPBRMaterial(forwardSurfaceAndLighting, assets);
  102. // Load an environment map
  103. assets.skyTex = ExampleFramework::loadTexture(ExampleTexture::EnvironmentPaperMill, false, true, true);
  104. return assets;
  105. }
  106. HRenderable gRenderable;
  107. HGUIWidget gGUI;
  108. UINT32 gMaterialIdx = 0;
  109. /** Set up the 3D object used by the example, and the camera to view the world through. */
  110. void setUp3DScene(const Assets& assets)
  111. {
  112. /************************************************************************/
  113. /* RENDERABLE */
  114. /************************************************************************/
  115. // Now we create a scene object that has a position, orientation, scale and optionally components to govern its
  116. // logic. In this particular case we are creating a SceneObject with a Renderable component which will render a
  117. // mesh at the position of the scene object with the provided material.
  118. // Create new scene object at (0, 0, 0)
  119. HSceneObject pistolSO = SceneObject::create("Pistol");
  120. // Attach the Renderable component and hook up the mesh we loaded, and the material we created.
  121. gRenderable = pistolSO->addComponent<CRenderable>();
  122. gRenderable->setMesh(assets.sphere);
  123. gRenderable->setMaterial(assets.standardMaterial);
  124. // Add a rotator component so we can rotate the object during runtime
  125. pistolSO->addComponent<ObjectRotator>();
  126. /************************************************************************/
  127. /* LIGHT */
  128. /************************************************************************/
  129. // Add a light so we can actually see the object
  130. HSceneObject lightSO = SceneObject::create("Light");
  131. HLight light = lightSO->addComponent<CLight>();
  132. light->setIntensity(100.0f);
  133. lightSO->setPosition(Vector3(1.0f, 0.5f, 0.0f));
  134. /************************************************************************/
  135. /* SKYBOX */
  136. /************************************************************************/
  137. // Add a skybox texture for sky reflections
  138. HSceneObject skyboxSO = SceneObject::create("Skybox");
  139. HSkybox skybox = skyboxSO->addComponent<CSkybox>();
  140. skybox->setTexture(assets.skyTex);
  141. /************************************************************************/
  142. /* CAMERA */
  143. /************************************************************************/
  144. // In order something to render on screen we need at least one camera.
  145. // Like before, we create a new scene object at (0, 0, 0).
  146. HSceneObject sceneCameraSO = SceneObject::create("SceneCamera");
  147. // Get the primary render window we need for creating the camera.
  148. SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
  149. // Add a Camera component that will output whatever it sees into that window
  150. // (You could also use a render texture or another window you created).
  151. HCamera sceneCamera = sceneCameraSO->addComponent<CCamera>();
  152. sceneCamera->getViewport()->setTarget(window);
  153. // Set up camera component properties
  154. // Set closest distance that is visible. Anything below that is clipped.
  155. sceneCamera->setNearClipDistance(0.005f);
  156. // Set farthest distance that is visible. Anything above that is clipped.
  157. sceneCamera->setFarClipDistance(1000);
  158. // Set aspect ratio depending on the current resolution
  159. sceneCamera->setAspectRatio(windowResWidth / (float)windowResHeight);
  160. // Add a CameraFlyer component that allows us to move the camera. See CameraFlyer for more information.
  161. sceneCameraSO->addComponent<CameraFlyer>();
  162. // Position and orient the camera scene object
  163. sceneCameraSO->setPosition(Vector3(2.0f, 1.0f, 2.0f));
  164. sceneCameraSO->lookAt(Vector3(-0.4f, 0, 0));
  165. /************************************************************************/
  166. /* GUI */
  167. /************************************************************************/
  168. // Add a GUIWidget component we will use for rendering the GUI
  169. HSceneObject guiSO = SceneObject::create("GUI");
  170. gGUI = guiSO->addComponent<CGUIWidget>(sceneCamera);
  171. }
  172. /** Sets up or rebuilds any GUI elements used by the example. */
  173. void updateGUI()
  174. {
  175. GUIPanel* mainPanel = gGUI->getPanel();
  176. // Clear any existing elements, in case this is not the first time we're calling this function
  177. mainPanel->clear();
  178. // Set up strings to display
  179. String materialNameLookup[] =
  180. {
  181. "Standard",
  182. "Vertex wobble (Deferred)",
  183. "Surface noise (Deferred)",
  184. "Lambert BRDF (Deferred)",
  185. "Surface noise & Lambert BRDF (Forward)"
  186. };
  187. HString toggleString("Press Q to toggle between materials");
  188. HString currentMaterialString("Current material: {0}");
  189. currentMaterialString.setParameter(0, materialNameLookup[gMaterialIdx]);
  190. // Create a vertical GUI layout to align the two labels one below each other
  191. GUILayoutY* vertLayout = GUILayoutY::create();
  192. // Create a couple of GUI labels displaying the two strings we created above
  193. vertLayout->addNewElement<GUILabel>(toggleString);
  194. vertLayout->addNewElement<GUILabel>(currentMaterialString);
  195. // Register the layout with the main GUI panel, placing the layout in top left corner of the screen by default
  196. mainPanel->addElement(vertLayout);
  197. }
  198. /** Switches the material used for rendering the renderable object. */
  199. void switchMaterial()
  200. {
  201. HMaterial materialLookup[] =
  202. {
  203. gAssets.standardMaterial,
  204. gAssets.vertexMaterial,
  205. gAssets.deferredSurfaceMaterial,
  206. gAssets.forwardMaterial
  207. };
  208. gMaterialIdx = (gMaterialIdx + 1) % 5;
  209. // Apply the newly selected material
  210. switch(gMaterialIdx)
  211. {
  212. case 0:
  213. // Standard material, simply apply to renderable
  214. gRenderable->setMaterial(gAssets.standardMaterial);
  215. break;
  216. case 1:
  217. // Deferred vertex material, simply apply to renderable
  218. gRenderable->setMaterial(gAssets.vertexMaterial);
  219. break;
  220. case 2:
  221. // Deferred surface material, simply apply to renderable
  222. gRenderable->setMaterial(gAssets.deferredSurfaceMaterial);
  223. break;
  224. case 3:
  225. // Deferred lighting material. Apply it globally and reset the surface material back to standard.
  226. gRenderable->setMaterial(gAssets.standardMaterial);
  227. ct::gRenderer()->setGlobalShaderOverride(gAssets.deferredLightingShader.getInternalPtr());
  228. break;
  229. case 4:
  230. // Forward surface/lighting material. Simply apply to renderable. Also clear the deferred lighting material
  231. // override from the last material.
  232. gRenderable->setMaterial(gAssets.forwardMaterial);
  233. // Clear previous overrides
  234. const Vector<SubShader>& subShaders = gAssets.deferredLightingShader->getSubShaders();
  235. for(auto& entry : subShaders)
  236. ct::gRenderer()->setGlobalShaderOverride(entry.name, nullptr);
  237. break;
  238. }
  239. // Update GUI with current material name
  240. updateGUI();
  241. }
  242. /** Register relevant mouse/keyboard buttons used for controlling the example. */
  243. void setupInput()
  244. {
  245. // Registers a default set of input controls
  246. ExampleFramework::setupInputConfig();
  247. static VirtualButton SwitchMaterialButton("SwitchMaterial");
  248. // Register a key for toggling between different materials
  249. auto inputConfig = gVirtualInput().getConfiguration();
  250. inputConfig->registerButton("SwitchMaterial", BC_Q);
  251. gVirtualInput().onButtonUp.connect(
  252. [](const VirtualButton& btn, UINT32 deviceIdx)
  253. {
  254. if(btn == SwitchMaterialButton)
  255. switchMaterial();
  256. });
  257. }
  258. }
  259. /** Main entry point into the application. */
  260. #if BS_PLATFORM == BS_PLATFORM_WIN32
  261. #include <windows.h>
  262. int CALLBACK WinMain(
  263. _In_ HINSTANCE hInstance,
  264. _In_ HINSTANCE hPrevInstance,
  265. _In_ LPSTR lpCmdLine,
  266. _In_ int nCmdShow
  267. )
  268. #else
  269. int main()
  270. #endif
  271. {
  272. using namespace bs;
  273. // Initializes the application and creates a window with the specified properties
  274. VideoMode videoMode(windowResWidth, windowResHeight);
  275. Application::startUp(videoMode, "Example", false);
  276. // Register buttons for controlling the example
  277. setupInput();
  278. // Load a model and textures, create materials
  279. gAssets = loadAssets();
  280. // Set up the scene with an object to render and a camera
  281. setUp3DScene(gAssets);
  282. // Sets up any GUI elements used by the example.
  283. updateGUI();
  284. // Runs the main loop that does most of the work. This method will exit when user closes the main
  285. // window or exits in some other way.
  286. Application::instance().runMainLoop();
  287. // When done, clean up
  288. Application::shutDown();
  289. return 0;
  290. }