1 | /******************************************************************************************* |
2 | * |
3 | * raylib [models] example - PBR material |
4 | * |
5 | * NOTE: This example requires raylib OpenGL 3.3 for shaders support and only #version 330 |
6 | * is currently supported. OpenGL ES 2.0 platforms are not supported at the moment. |
7 | * |
8 | * This example has been created using raylib 1.8 (www.raylib.com) |
9 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) |
10 | * |
11 | * Copyright (c) 2017 Ramon Santamaria (@raysan5) |
12 | * |
13 | ********************************************************************************************/ |
14 | |
15 | #include "raylib.h" |
16 | #include "raymath.h" |
17 | |
18 | #include <stdio.h> |
19 | |
20 | #define RLIGHTS_IMPLEMENTATION |
21 | #include "rlights.h" |
22 | |
23 | #define CUBEMAP_SIZE 512 // Cubemap texture size |
24 | #define IRRADIANCE_SIZE 32 // Irradiance texture size |
25 | #define PREFILTERED_SIZE 256 // Prefiltered HDR environment texture size |
26 | #define BRDF_SIZE 512 // BRDF LUT texture size |
27 | |
28 | // PBR material loading |
29 | static Material LoadMaterialPBR(Color albedo, float metalness, float roughness); |
30 | |
31 | int main(void) |
32 | { |
33 | // Initialization |
34 | //-------------------------------------------------------------------------------------- |
35 | const int screenWidth = 800; |
36 | const int screenHeight = 450; |
37 | |
38 | SetConfigFlags(FLAG_MSAA_4X_HINT); // Enable Multi Sampling Anti Aliasing 4x (if available) |
39 | InitWindow(screenWidth, screenHeight, "raylib [models] example - pbr material" ); |
40 | |
41 | // Define the camera to look into our 3d world |
42 | Camera camera = { 0 }; |
43 | camera.position = (Vector3){ 4.0f, 4.0f, 4.0f }; // Camera position |
44 | camera.target = (Vector3){ 0.0f, 0.5f, 0.0f }; // Camera looking at point |
45 | camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) |
46 | camera.fovy = 45.0f; // Camera field-of-view Y |
47 | camera.type = CAMERA_PERSPECTIVE; // Camera mode type |
48 | |
49 | // Load model and PBR material |
50 | Model model = LoadModel("resources/pbr/trooper.obj" ); |
51 | |
52 | // Mesh tangents are generated... and uploaded to GPU |
53 | // NOTE: New VBO for tangents is generated at default location and also binded to mesh VAO |
54 | MeshTangents(&model.meshes[0]); |
55 | |
56 | UnloadMaterial(model.materials[0]); // get rid of default material |
57 | model.materials[0] = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f); |
58 | |
59 | // Create lights |
60 | // NOTE: Lights are added to an internal lights pool automatically |
61 | CreateLight(LIGHT_POINT, (Vector3){ LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 0, 255 }, model.materials[0].shader); |
62 | CreateLight(LIGHT_POINT, (Vector3){ 0.0f, LIGHT_HEIGHT, LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 255, 0, 255 }, model.materials[0].shader); |
63 | CreateLight(LIGHT_POINT, (Vector3){ -LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 0, 255, 255 }, model.materials[0].shader); |
64 | CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 0.0f, LIGHT_HEIGHT*2.0f, -LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 255, 255 }, model.materials[0].shader); |
65 | |
66 | SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode |
67 | |
68 | SetTargetFPS(60); // Set our game to run at 60 frames-per-second |
69 | //-------------------------------------------------------------------------------------- |
70 | |
71 | // Main game loop |
72 | while (!WindowShouldClose()) // Detect window close button or ESC key |
73 | { |
74 | // Update |
75 | //---------------------------------------------------------------------------------- |
76 | UpdateCamera(&camera); // Update camera |
77 | |
78 | // Send to material PBR shader camera view position |
79 | float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; |
80 | SetShaderValue(model.materials[0].shader, model.materials[0].shader.locs[LOC_VECTOR_VIEW], cameraPos, UNIFORM_VEC3); |
81 | //---------------------------------------------------------------------------------- |
82 | |
83 | // Draw |
84 | //---------------------------------------------------------------------------------- |
85 | BeginDrawing(); |
86 | |
87 | ClearBackground(RAYWHITE); |
88 | |
89 | BeginMode3D(camera); |
90 | |
91 | DrawModel(model, Vector3Zero(), 1.0f, WHITE); |
92 | |
93 | DrawGrid(10, 1.0f); |
94 | |
95 | EndMode3D(); |
96 | |
97 | DrawFPS(10, 10); |
98 | |
99 | EndDrawing(); |
100 | //---------------------------------------------------------------------------------- |
101 | } |
102 | |
103 | // De-Initialization |
104 | //-------------------------------------------------------------------------------------- |
105 | |
106 | // Shaders and textures must be unloaded by user, |
107 | // they could be in use by other models |
108 | UnloadTexture(model.materials[0].maps[MAP_ALBEDO].texture); |
109 | UnloadTexture(model.materials[0].maps[MAP_NORMAL].texture); |
110 | UnloadTexture(model.materials[0].maps[MAP_METALNESS].texture); |
111 | UnloadTexture(model.materials[0].maps[MAP_ROUGHNESS].texture); |
112 | UnloadTexture(model.materials[0].maps[MAP_OCCLUSION].texture); |
113 | UnloadTexture(model.materials[0].maps[MAP_IRRADIANCE].texture); |
114 | UnloadTexture(model.materials[0].maps[MAP_PREFILTER].texture); |
115 | UnloadTexture(model.materials[0].maps[MAP_BRDF].texture); |
116 | UnloadShader(model.materials[0].shader); |
117 | |
118 | UnloadModel(model); // Unload model |
119 | |
120 | CloseWindow(); // Close window and OpenGL context |
121 | //-------------------------------------------------------------------------------------- |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | // Load PBR material (Supports: ALBEDO, NORMAL, METALNESS, ROUGHNESS, AO, EMMISIVE, HEIGHT maps) |
127 | // NOTE: PBR shader is loaded inside this function |
128 | static Material LoadMaterialPBR(Color albedo, float metalness, float roughness) |
129 | { |
130 | Material mat = LoadMaterialDefault(); // Initialize material to default |
131 | |
132 | #if defined(PLATFORM_DESKTOP) |
133 | mat.shader = LoadShader("resources/shaders/glsl330/pbr.vs" , "resources/shaders/glsl330/pbr.fs" ); |
134 | #else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB |
135 | mat.shader = LoadShader("resources/shaders/glsl100/pbr.vs" , "resources/shaders/glsl100/pbr.fs" ); |
136 | #endif |
137 | |
138 | // Get required locations points for PBR material |
139 | // NOTE: Those location names must be available and used in the shader code |
140 | mat.shader.locs[LOC_MAP_ALBEDO] = GetShaderLocation(mat.shader, "albedo.sampler" ); |
141 | mat.shader.locs[LOC_MAP_METALNESS] = GetShaderLocation(mat.shader, "metalness.sampler" ); |
142 | mat.shader.locs[LOC_MAP_NORMAL] = GetShaderLocation(mat.shader, "normals.sampler" ); |
143 | mat.shader.locs[LOC_MAP_ROUGHNESS] = GetShaderLocation(mat.shader, "roughness.sampler" ); |
144 | mat.shader.locs[LOC_MAP_OCCLUSION] = GetShaderLocation(mat.shader, "occlusion.sampler" ); |
145 | //mat.shader.locs[LOC_MAP_EMISSION] = GetShaderLocation(mat.shader, "emission.sampler"); |
146 | //mat.shader.locs[LOC_MAP_HEIGHT] = GetShaderLocation(mat.shader, "height.sampler"); |
147 | mat.shader.locs[LOC_MAP_IRRADIANCE] = GetShaderLocation(mat.shader, "irradianceMap" ); |
148 | mat.shader.locs[LOC_MAP_PREFILTER] = GetShaderLocation(mat.shader, "prefilterMap" ); |
149 | mat.shader.locs[LOC_MAP_BRDF] = GetShaderLocation(mat.shader, "brdfLUT" ); |
150 | |
151 | // Set view matrix location |
152 | mat.shader.locs[LOC_MATRIX_MODEL] = GetShaderLocation(mat.shader, "matModel" ); |
153 | //mat.shader.locs[LOC_MATRIX_VIEW] = GetShaderLocation(mat.shader, "view"); |
154 | mat.shader.locs[LOC_VECTOR_VIEW] = GetShaderLocation(mat.shader, "viewPos" ); |
155 | |
156 | // Set PBR standard maps |
157 | mat.maps[MAP_ALBEDO].texture = LoadTexture("resources/pbr/trooper_albedo.png" ); |
158 | mat.maps[MAP_NORMAL].texture = LoadTexture("resources/pbr/trooper_normals.png" ); |
159 | mat.maps[MAP_METALNESS].texture = LoadTexture("resources/pbr/trooper_metalness.png" ); |
160 | mat.maps[MAP_ROUGHNESS].texture = LoadTexture("resources/pbr/trooper_roughness.png" ); |
161 | mat.maps[MAP_OCCLUSION].texture = LoadTexture("resources/pbr/trooper_ao.png" ); |
162 | |
163 | // Load equirectangular to cubemap shader |
164 | #if defined(PLATFORM_DESKTOP) |
165 | Shader shdrCubemap = LoadShader("resources/shaders/glsl330/cubemap.vs" , "resources/shaders/glsl330/cubemap.fs" ); |
166 | #else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB |
167 | Shader shdrCubemap = LoadShader("resources/shaders/glsl100/cubemap.vs" , "resources/shaders/glsl100/cubemap.fs" ); |
168 | #endif |
169 | |
170 | // Load irradiance (GI) calculation shader |
171 | #if defined(PLATFORM_DESKTOP) |
172 | Shader shdrIrradiance = LoadShader("resources/shaders/glsl330/skybox.vs" , "resources/shaders/glsl330/irradiance.fs" ); |
173 | #else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB |
174 | Shader shdrIrradiance = LoadShader("resources/shaders/glsl100/skybox.vs" , "resources/shaders/glsl100/irradiance.fs" ); |
175 | #endif |
176 | |
177 | // Load reflection prefilter calculation shader |
178 | #if defined(PLATFORM_DESKTOP) |
179 | Shader shdrPrefilter = LoadShader("resources/shaders/glsl330/skybox.vs" , "resources/shaders/glsl330/prefilter.fs" ); |
180 | #else |
181 | Shader shdrPrefilter = LoadShader("resources/shaders/glsl100/skybox.vs" , "resources/shaders/glsl100/prefilter.fs" ); |
182 | #endif |
183 | |
184 | // Load bidirectional reflectance distribution function shader |
185 | #if defined(PLATFORM_DESKTOP) |
186 | Shader shdrBRDF = LoadShader("resources/shaders/glsl330/brdf.vs" , "resources/shaders/glsl330/brdf.fs" ); |
187 | #else |
188 | Shader shdrBRDF = LoadShader("resources/shaders/glsl100/brdf.vs" , "resources/shaders/glsl100/brdf.fs" ); |
189 | #endif |
190 | |
191 | // Setup required shader locations |
192 | SetShaderValue(shdrCubemap, GetShaderLocation(shdrCubemap, "equirectangularMap" ), (int[1]){ 0 }, UNIFORM_INT); |
193 | SetShaderValue(shdrIrradiance, GetShaderLocation(shdrIrradiance, "environmentMap" ), (int[1]){ 0 }, UNIFORM_INT); |
194 | SetShaderValue(shdrPrefilter, GetShaderLocation(shdrPrefilter, "environmentMap" ), (int[1]){ 0 }, UNIFORM_INT); |
195 | |
196 | Texture2D texHDR = LoadTexture("resources/dresden_square.hdr" ); |
197 | Texture2D cubemap = GenTextureCubemap(shdrCubemap, texHDR, CUBEMAP_SIZE); |
198 | mat.maps[MAP_IRRADIANCE].texture = GenTextureIrradiance(shdrIrradiance, cubemap, IRRADIANCE_SIZE); |
199 | mat.maps[MAP_PREFILTER].texture = GenTexturePrefilter(shdrPrefilter, cubemap, PREFILTERED_SIZE); |
200 | mat.maps[MAP_BRDF].texture = GenTextureBRDF(shdrBRDF, BRDF_SIZE); |
201 | UnloadTexture(cubemap); |
202 | UnloadTexture(texHDR); |
203 | |
204 | // Unload already used shaders (to create specific textures) |
205 | UnloadShader(shdrCubemap); |
206 | UnloadShader(shdrIrradiance); |
207 | UnloadShader(shdrPrefilter); |
208 | UnloadShader(shdrBRDF); |
209 | |
210 | // Set textures filtering for better quality |
211 | SetTextureFilter(mat.maps[MAP_ALBEDO].texture, FILTER_BILINEAR); |
212 | SetTextureFilter(mat.maps[MAP_NORMAL].texture, FILTER_BILINEAR); |
213 | SetTextureFilter(mat.maps[MAP_METALNESS].texture, FILTER_BILINEAR); |
214 | SetTextureFilter(mat.maps[MAP_ROUGHNESS].texture, FILTER_BILINEAR); |
215 | SetTextureFilter(mat.maps[MAP_OCCLUSION].texture, FILTER_BILINEAR); |
216 | |
217 | // Enable sample usage in shader for assigned textures |
218 | SetShaderValue(mat.shader, GetShaderLocation(mat.shader, "albedo.useSampler" ), (int[1]){ 1 }, UNIFORM_INT); |
219 | SetShaderValue(mat.shader, GetShaderLocation(mat.shader, "normals.useSampler" ), (int[1]){ 1 }, UNIFORM_INT); |
220 | SetShaderValue(mat.shader, GetShaderLocation(mat.shader, "metalness.useSampler" ), (int[1]){ 1 }, UNIFORM_INT); |
221 | SetShaderValue(mat.shader, GetShaderLocation(mat.shader, "roughness.useSampler" ), (int[1]){ 1 }, UNIFORM_INT); |
222 | SetShaderValue(mat.shader, GetShaderLocation(mat.shader, "occlusion.useSampler" ), (int[1]){ 1 }, UNIFORM_INT); |
223 | |
224 | int renderModeLoc = GetShaderLocation(mat.shader, "renderMode" ); |
225 | SetShaderValue(mat.shader, renderModeLoc, (int[1]){ 0 }, UNIFORM_INT); |
226 | |
227 | // Set up material properties color |
228 | mat.maps[MAP_ALBEDO].color = albedo; |
229 | mat.maps[MAP_NORMAL].color = (Color){ 128, 128, 255, 255 }; |
230 | mat.maps[MAP_METALNESS].value = metalness; |
231 | mat.maps[MAP_ROUGHNESS].value = roughness; |
232 | mat.maps[MAP_OCCLUSION].value = 1.0f; |
233 | mat.maps[MAP_EMISSION].value = 0.5f; |
234 | mat.maps[MAP_HEIGHT].value = 0.5f; |
235 | |
236 | return mat; |
237 | } |
238 | |