shaders_basic_pbr.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*******************************************************************************************
  2. *
  3. * raylib [core] example - Model Defuse Normal Shader (adapted for HTML5 platform)
  4. *
  5. * This example is prepared to compile for PLATFORM_WEB and PLATFORM_DESKTOP
  6. * As you will notice, code structure is slightly different to the other examples...
  7. * To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning
  8. *
  9. * This example has been created using raylib 5.0 (www.raylib.com)
  10. * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
  11. *
  12. * Copyright (c) 2023-2024 Afan OLOVCIC (@_DevDad) 2015 Ramon Santamaria (@raysan5)
  13. * Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox is licensed under Creative Commons Attribution-NonCommercial (http://creativecommons.org/licenses/by-nc/4.0/).
  14. ********************************************************************************************/
  15. #include "raylib.h"
  16. #if defined(PLATFORM_WEB)
  17. #include <emscripten/emscripten.h>
  18. #endif
  19. #if defined(PLATFORM_DESKTOP)
  20. #define GLSL_VERSION 330
  21. #else // PLATFORM_ANDROID, PLATFORM_WEB
  22. #define GLSL_VERSION 120
  23. #endif
  24. #include <stdlib.h> // Required for: NULL
  25. #define MAX_LIGHTS 4 // Max dynamic lights supported by shader
  26. int lightsCount; // Current number of dynamic lights that have been created
  27. typedef struct {
  28. int enabled;
  29. int type;
  30. Vector3 position;
  31. Vector3 target;
  32. float color[4];
  33. float intensity;
  34. int enabledLoc;
  35. int typeLoc;
  36. int positionLoc;
  37. int targetLoc;
  38. int colorLoc;
  39. int intensityLoc;
  40. } PBRLight;
  41. typedef enum {
  42. LIGHT_DIRECTIONAL = 0,
  43. LIGHT_POINT,
  44. LIGHT_SPOT
  45. } PBRLightType;
  46. // Create a light and get shader locations
  47. PBRLight PBRLightCreate(int type, Vector3 position, Vector3 target, Color color, float intensity, Shader shader);
  48. // Send light properties to shader
  49. // NOTE: Light shader locations should be available
  50. void PBRLightUpdate(Shader shader, PBRLight light);
  51. //----------------------------------------------------------------------------------
  52. // Main Entry Point
  53. //----------------------------------------------------------------------------------
  54. int main()
  55. {
  56. // Initialization
  57. //--------------------------------------------------------------------------------------
  58. const int screenWidth = 800;
  59. const int screenHeight = 450;
  60. SetConfigFlags(FLAG_MSAA_4X_HINT);
  61. InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr");
  62. // Define the camera to look into our 3d world
  63. Camera camera = { 0 };
  64. camera.position = (Vector3){ 2.0f, 2.0f, 6.0f }; // Camera position
  65. camera.target = (Vector3){ 0.0f, 0.5f, 0.0f }; // Camera looking at point
  66. camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
  67. camera.fovy = 45.0f; // Camera field-of-view Y
  68. camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
  69. Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/pbr.vs",GLSL_VERSION),
  70. TextFormat("resources/shaders/glsl%i/pbr.fs",GLSL_VERSION));
  71. shader.locs[SHADER_LOC_MAP_ALBEDO] = GetShaderLocation(shader, "albedoMap");
  72. // In reality, metalness, roughness, and ambient occlusion are all packed into the MRA texture
  73. // We'll pass it in as the metalness map
  74. shader.locs[SHADER_LOC_MAP_METALNESS] = GetShaderLocation(shader, "mraMap");
  75. shader.locs[SHADER_LOC_MAP_NORMAL] = GetShaderLocation(shader, "normalMap");
  76. // Similarly to the MRA map, the emissive map packs different information into a single texture
  77. // This map stores both height and emission in reality
  78. shader.locs[SHADER_LOC_MAP_EMISSION] = GetShaderLocation(shader, "emissiveMap");
  79. shader.locs[SHADER_LOC_COLOR_DIFFUSE] = GetShaderLocation(shader, "albedoColor");
  80. shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
  81. int numOfLightsLoc = GetShaderLocation(shader, "numOfLights");
  82. int numOfLights = 4;
  83. SetShaderValue(shader, numOfLightsLoc, &numOfLights, SHADER_UNIFORM_INT);
  84. Color ambCol = (Color){ 26,32,135,255 };
  85. Vector3 ambColNormalized = (Vector3){ ambCol.r / 255.0f, ambCol.g / 255.0f, ambCol.b / 255.0f };
  86. float ambIntens = 0.02;
  87. int albedoLoc = GetShaderLocation(shader, "albedo");
  88. int ambColLoc = GetShaderLocation(shader, "ambientColor");
  89. int ambLoc = GetShaderLocation(shader, "ambient");
  90. SetShaderValue(shader, ambColLoc, &ambColNormalized, SHADER_UNIFORM_VEC3);
  91. SetShaderValue(shader, ambLoc, &ambIntens, SHADER_UNIFORM_FLOAT);
  92. int emissiveIntensityLoc = GetShaderLocation(shader, "emissivePower");
  93. int emissiveColorLoc = GetShaderLocation(shader, "emissiveColor");
  94. int textureTilingLoc = GetShaderLocation(shader, "tiling");
  95. Model model = LoadModel("resources/models/old_car_new.glb");
  96. // If the OBJ file format is used, we will have to generate tangents manually:
  97. // GenMeshTangents(&model.meshes[0]);
  98. model.materials[0].shader = shader;
  99. model.materials[0].maps[MATERIAL_MAP_ALBEDO].color = WHITE;
  100. model.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.0f;
  101. model.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.0f;
  102. model.materials[0].maps[MATERIAL_MAP_OCCLUSION].value = 1.0f;
  103. model.materials[0].maps[MATERIAL_MAP_EMISSION].color = (Color){ 255, 162, 0, 255 };
  104. model.materials[0].maps[MATERIAL_MAP_ALBEDO].texture = LoadTexture("resources/old_car_d.png");
  105. model.materials[0].maps[MATERIAL_MAP_METALNESS].texture = LoadTexture("resources/old_car_mra.png");
  106. model.materials[0].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture("resources/old_car_n.png");
  107. model.materials[0].maps[MATERIAL_MAP_EMISSION].texture = LoadTexture("resources/old_car_e.png");
  108. // We store tiling parameters in the generic parameter slots in the Material class
  109. Vector2 modelTiling = (Vector2){ 0.5f, 0.5f };
  110. Model floor = LoadModel("resources/models/plane.glb");
  111. floor.materials[0].shader = shader;
  112. floor.materials[0].maps[MATERIAL_MAP_ALBEDO].color = WHITE;
  113. floor.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.0f;
  114. floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.0f;
  115. floor.materials[0].maps[MATERIAL_MAP_OCCLUSION].value = 1.0f;
  116. floor.materials[0].maps[MATERIAL_MAP_EMISSION].color = BLACK;
  117. floor.materials[0].maps[MATERIAL_MAP_ALBEDO].texture = LoadTexture("resources/road_a.png");
  118. floor.materials[0].maps[MATERIAL_MAP_METALNESS].texture = LoadTexture("resources/road_mra.png");
  119. floor.materials[0].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture("resources/road_n.png");
  120. Vector2 floorTiling = (Vector2){ 0.5f, 0.5f };
  121. // Create lights
  122. PBRLight lights[MAX_LIGHTS] = { 0 };
  123. lights[0] = PBRLightCreate(LIGHT_POINT, (Vector3){ -1, 1, -2 }, (Vector3){0,0,0}, YELLOW,4, shader);
  124. lights[1] = PBRLightCreate(LIGHT_POINT, (Vector3){ 2, 1, 1 }, (Vector3){0,0,0}, GREEN,3.3, shader);
  125. lights[2] = PBRLightCreate(LIGHT_POINT, (Vector3){ -2, 1, 1 }, (Vector3){0,0,0}, RED,8.3, shader);
  126. lights[3] = PBRLightCreate(LIGHT_POINT, (Vector3){ 1, 1, -2 }, (Vector3){0,0,0}, BLUE,2, shader);
  127. // The textures are always used
  128. int one = 1;
  129. SetShaderValue(shader, GetShaderLocation(shader, "useTexAlbedo"), &one, SHADER_UNIFORM_INT);
  130. SetShaderValue(shader, GetShaderLocation(shader, "useTexNormal"), &one, SHADER_UNIFORM_INT);
  131. SetShaderValue(shader, GetShaderLocation(shader, "useTexMRA"), &one, SHADER_UNIFORM_INT);
  132. SetShaderValue(shader, GetShaderLocation(shader, "useTexEmissive"), &one, SHADER_UNIFORM_INT);
  133. SetTargetFPS(60); // Set our game to run at 60 frames-per-second-------------------------------------------------------------
  134. int emissiveCnt = 0;
  135. // Main game loop
  136. while (!WindowShouldClose()) // Detect window close button or ESC key
  137. {
  138. // Update
  139. //----------------------------------------------------------------------------------
  140. UpdateCamera(&camera, CAMERA_ORBITAL);
  141. // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
  142. float cameraPos[3] = {camera.position.x, camera.position.y, camera.position.z};
  143. SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
  144. // Check key inputs to enable/disable lights
  145. if (IsKeyPressed(KEY_Y)) { lights[0].enabled = !lights[0].enabled; }
  146. if (IsKeyPressed(KEY_G)) { lights[1].enabled = !lights[1].enabled; }
  147. if (IsKeyPressed(KEY_R)) { lights[2].enabled = !lights[2].enabled; }
  148. if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; }
  149. // Update light values (actually, only enable/disable them)
  150. for (int i = 0; i < MAX_LIGHTS; i++) PBRLightUpdate(shader, lights[i]);
  151. //----------------------------------------------------------------------------------
  152. // Draw
  153. //----------------------------------------------------------------------------------
  154. BeginDrawing();
  155. ClearBackground(BLACK);
  156. BeginMode3D(camera);
  157. SetShaderValue(shader, textureTilingLoc, &floorTiling, SHADER_UNIFORM_VEC2);
  158. Vector4 floorEmission = ColorNormalize(floor.materials[0].maps[MATERIAL_MAP_EMISSION].color);
  159. SetShaderValue(shader, emissiveColorLoc, &floorEmission, SHADER_UNIFORM_VEC4);
  160. DrawModel(floor, (Vector3){0,0,0}, 5.0f, WHITE);
  161. emissiveCnt--;
  162. if (emissiveCnt <= 0)
  163. {
  164. emissiveCnt = GetRandomValue(0, 20);
  165. float intensity = (float)GetRandomValue(0, 100) / 100;
  166. SetShaderValue(shader, emissiveIntensityLoc, &intensity, SHADER_UNIFORM_FLOAT);
  167. }
  168. SetShaderValue(shader, textureTilingLoc, &modelTiling, SHADER_UNIFORM_VEC2);
  169. Vector4 modelEmission = ColorNormalize(model.materials[0].maps[MATERIAL_MAP_EMISSION].color);
  170. SetShaderValue(shader, emissiveColorLoc, &modelEmission, SHADER_UNIFORM_VEC4);
  171. DrawModel(model, (Vector3) {0, 0.0, 0}, 0.005, WHITE);
  172. // Draw spheres to show where the lights are
  173. for (int i = 0; i < MAX_LIGHTS; i++)
  174. {
  175. Color col = (Color) {lights[i].color[0] * 255, lights[i].color[1] * 255, lights[i].color[2] * 255,
  176. lights[i].color[3] * 255};
  177. if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, col);
  178. else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(col, 0.3f));
  179. }
  180. EndMode3D();
  181. DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, LIGHTGRAY);
  182. DrawFPS(10, 10);
  183. EndDrawing();
  184. //----------------------------------------------------------------------------------
  185. }
  186. //--------------------------------------------------------------------------------------
  187. // De-Initialization
  188. //--------------------------------------------------------------------------------------
  189. model.materials[0].shader = (Shader){ 0 };
  190. floor.materials[0].shader = (Shader){ 0 };
  191. UnloadMaterial(model.materials[0]);
  192. UnloadMaterial(floor.materials[0]);
  193. model.materials[0].maps = NULL;
  194. floor.materials[0].maps = NULL;
  195. UnloadModel(floor); // Unload model
  196. UnloadModel(model); // Unload model
  197. UnloadShader(shader); // Unload Shader
  198. CloseWindow(); // Close window and OpenGL context
  199. //--------------------------------------------------------------------------------------
  200. return 0;
  201. }
  202. PBRLight PBRLightCreate(int type, Vector3 position, Vector3 target, Color color, float intensity, Shader shader)
  203. {
  204. PBRLight light = { 0 };
  205. if (lightsCount < MAX_LIGHTS)
  206. {
  207. light.enabled = 1;
  208. light.type = type;
  209. light.position = position;
  210. light.target = target;
  211. light.color[0] = (float)color.r / (float)255;
  212. light.color[1] = (float)color.g / (float)255;
  213. light.color[2] = (float)color.b / (float)255;
  214. light.color[3] = (float)color.a / (float)255;
  215. light.intensity = intensity;
  216. // NOTE: Lighting shader naming must be the provided ones
  217. light.enabledLoc = GetShaderLocation(shader, TextFormat("lights[%i].enabled", lightsCount));
  218. light.typeLoc = GetShaderLocation(shader, TextFormat("lights[%i].type", lightsCount));
  219. light.positionLoc = GetShaderLocation(shader, TextFormat("lights[%i].position", lightsCount));
  220. light.targetLoc = GetShaderLocation(shader, TextFormat("lights[%i].target", lightsCount));
  221. light.colorLoc = GetShaderLocation(shader, TextFormat("lights[%i].color", lightsCount));
  222. light.intensityLoc = GetShaderLocation(shader, TextFormat("lights[%i].intensity", lightsCount));
  223. PBRLightUpdate(shader, light);
  224. lightsCount++;
  225. }
  226. return light;
  227. }
  228. // Send light properties to shader
  229. // NOTE: Light shader locations should be available
  230. void PBRLightUpdate(Shader shader, PBRLight light)
  231. {
  232. SetShaderValue(shader, light.enabledLoc, &light.enabled, SHADER_UNIFORM_INT);
  233. SetShaderValue(shader, light.typeLoc, &light.type, SHADER_UNIFORM_INT);
  234. // Send to shader light position values
  235. float position[3] = { light.position.x, light.position.y, light.position.z };
  236. SetShaderValue(shader, light.positionLoc, position, SHADER_UNIFORM_VEC3);
  237. // Send to shader light target position values
  238. float target[3] = { light.target.x, light.target.y, light.target.z };
  239. SetShaderValue(shader, light.targetLoc, target, SHADER_UNIFORM_VEC3);
  240. SetShaderValue(shader, light.colorLoc, light.color, SHADER_UNIFORM_VEC4);
  241. SetShaderValue(shader, light.intensityLoc, &light.intensity, SHADER_UNIFORM_FLOAT);
  242. }