-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
basic PBR example #3621
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
basic PBR example #3621
Changes from 3 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| /******************************************************************************************* | ||
| * | ||
| * raylib [core] example - Model Defuse Normal Shader (adapted for HTML5 platform) | ||
| * | ||
| * This example is prepared to compile for PLATFORM_WEB and PLATFORM_DESKTOP | ||
| * As you will notice, code structure is slightly different to the other examples... | ||
| * To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning | ||
| * | ||
| * This example has been created using raylib 5.0 (www.raylib.com) | ||
| * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) | ||
| * | ||
| * Copyright (c) 2023-2024 Afan OLOVCIC (@_DevDad) 2015 Ramon Santamaria (@raysan5) | ||
| * 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/). | ||
| ********************************************************************************************/ | ||
|
|
||
| #include "raylib.h" | ||
|
|
||
| #if defined(PLATFORM_WEB) | ||
| #include <emscripten/emscripten.h> | ||
| #endif | ||
|
|
||
| #define RPBR_IMPLEMENTATION | ||
| #include "rpbr.h" | ||
|
|
||
| #if defined(PLATFORM_DESKTOP) | ||
| #define GLSL_VERSION 330 | ||
| #else // PLATFORM_ANDROID, PLATFORM_WEB | ||
| #define GLSL_VERSION 120 | ||
| #endif | ||
|
|
||
|
|
||
| //---------------------------------------------------------------------------------- | ||
| // Main Entry Point | ||
| //---------------------------------------------------------------------------------- | ||
| int main() | ||
| { | ||
| // Initialization | ||
| //-------------------------------------------------------------------------------------- | ||
| const int screenWidth = 800; | ||
| const int screenHeight = 450; | ||
|
|
||
| SetConfigFlags(FLAG_MSAA_4X_HINT); | ||
| InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr"); | ||
|
|
||
| // Define the camera to look into our 3d world | ||
| Camera camera = { 0 }; | ||
| camera.position = (Vector3){ 2.0f, 2.0f, 6.0f }; // Camera position | ||
| camera.target = (Vector3){ 0.0f, 0.5f, 0.0f }; // Camera looking at point | ||
| camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) | ||
| camera.fovy = 45.0f; // Camera field-of-view Y | ||
| camera.projection = CAMERA_PERSPECTIVE; // Camera projection type | ||
|
|
||
|
|
||
| Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/pbr.vs",GLSL_VERSION), | ||
| TextFormat("resources/shaders/glsl%i/pbr.fs",GLSL_VERSION)); | ||
|
|
||
|
|
||
|
|
||
| PBRModel model = PBRModelLoad("resources/models/old_car_new.glb"); | ||
| //if we use obj file formator if model doesn't have tangents we have to calculate MeshTangents | ||
| //by using raylib function GenMeshTangents(mesh) for example: obj file doesn't support tangents | ||
| //GenMeshTangents(&model.model.meshes[0]); | ||
|
|
||
| PBRMaterial model_mat = (PBRMaterial){0}; | ||
| PBRMaterialSetup(&model_mat, shader, NULL); //environment = NULL for now | ||
| PBRLoadTextures(&model_mat, PBR_TEXTURE_ALBEDO, "resources/old_car_d.png"); | ||
| PBRLoadTextures(&model_mat, PBR_TEXTURE_MRA, "resources/old_car_mra.png"); | ||
| PBRLoadTextures(&model_mat, PBR_TEXTURE_NORMAL, "resources/old_car_n.png"); | ||
| PBRLoadTextures(&model_mat, PBR_TEXTURE_EMISSIVE, "resources/old_car_e.png"); | ||
| PBRSetColor(&model_mat,PBR_COLOR_EMISSIVE, (Color){255,162,0,255}); | ||
| PBRSetVec2(&model_mat, PBR_VEC2_TILING,(Vector2){0.5,0.5}); | ||
| PBRSetMaterial(&model,&model_mat,0); | ||
|
|
||
| PBRModel floor = PBRModelLoad("resources/models/plane.glb"); | ||
| PBRMaterial floor_mat = (PBRMaterial){0}; | ||
| PBRMaterialSetup(&floor_mat, shader, NULL); | ||
| PBRLoadTextures(&floor_mat, PBR_TEXTURE_ALBEDO, "resources/road_a.png"); | ||
| PBRLoadTextures(&floor_mat, PBR_TEXTURE_MRA, "resources/road_mra.png"); | ||
| PBRLoadTextures(&floor_mat, PBR_TEXTURE_NORMAL, "resources/road_n.png"); | ||
| PBRSetVec2(&floor_mat, PBR_VEC2_TILING,(Vector2){0.5,0.5}); | ||
| PBRSetMaterial(&floor,&floor_mat,0); | ||
|
|
||
| shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos"); | ||
| int numOfLightsLoc = GetShaderLocation(shader, "numOfLights"); | ||
| int numOfLights = 4; | ||
| SetShaderValue(shader, numOfLightsLoc, &numOfLights, SHADER_UNIFORM_INT); | ||
|
|
||
| Color ambCol = (Color){26,32,135,255}; | ||
| float ambIntens = 0.02; | ||
|
|
||
| int albedoLoc = GetShaderLocation(shader, "albedo"); | ||
| PBRSetAmbient(shader,ambCol,ambIntens); | ||
|
|
||
| // Create lights | ||
| PBRLight lights[MAX_LIGHTS] = { 0 }; | ||
| lights[0] = PBRLightCreate(LIGHT_POINT, (Vector3){ -1, 1, -2 }, (Vector3){0,0,0}, YELLOW,4, shader); | ||
| lights[1] = PBRLightCreate(LIGHT_POINT, (Vector3){ 2, 1, 1 }, (Vector3){0,0,0}, GREEN,3.3, shader); | ||
| lights[2] = PBRLightCreate(LIGHT_POINT, (Vector3){ -2, 1, 1 }, (Vector3){0,0,0}, RED,8.3, shader); | ||
| lights[3] = PBRLightCreate(LIGHT_POINT, (Vector3){ 1, 1, -2 }, (Vector3){0,0,0}, BLUE,2, shader); | ||
| SetShaderValueV(shader, GetShaderLocation(shader, "lights"), lights, SHADER_UNIFORM_FLOAT, numOfLights); | ||
|
|
||
| SetTargetFPS(60); // Set our game to run at 60 frames-per-second------------------------------------------------------------- | ||
|
|
||
| int emissiveCnt = 0; | ||
| // Main game loop | ||
| while (!WindowShouldClose()) // Detect window close button or ESC key | ||
| { | ||
| // Update | ||
| //---------------------------------------------------------------------------------- | ||
| UpdateCamera(&camera, CAMERA_ORBITAL); | ||
|
|
||
| // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f }) | ||
| float cameraPos[3] = {camera.position.x, camera.position.y, camera.position.z}; | ||
| SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3); | ||
|
|
||
| // Check key inputs to enable/disable lights | ||
| if (IsKeyPressed(KEY_Y)) { lights[0].enabled = !lights[0].enabled; } | ||
| if (IsKeyPressed(KEY_G)) { lights[1].enabled = !lights[1].enabled; } | ||
| if (IsKeyPressed(KEY_R)) { lights[2].enabled = !lights[2].enabled; } | ||
| if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; } | ||
|
|
||
| // Update light values (actually, only enable/disable them) | ||
| for (int i = 0; i < MAX_LIGHTS; i++) PBRLightUpdate(shader, lights[i]); | ||
| emissiveCnt--; | ||
| if(emissiveCnt<=0){ | ||
| emissiveCnt = GetRandomValue(0,20); | ||
| PBRSetFloat(&model_mat,PBR_PARAM_EMISSIVE,(float)GetRandomValue(0,100)/100); | ||
| } | ||
| //---------------------------------------------------------------------------------- | ||
|
|
||
| // Draw | ||
| //---------------------------------------------------------------------------------- | ||
| BeginDrawing(); | ||
| ClearBackground(BLACK); | ||
| BeginMode3D(camera); | ||
|
|
||
| PBRDrawModel(floor, (Vector3){0,0,0}, 5.0f); | ||
| PBRDrawModel(model, (Vector3) {0, 0.0, 0}, 0.005); | ||
|
|
||
| // Draw spheres to show where the lights are | ||
| for (int i = 0; i < MAX_LIGHTS; i++) { | ||
| Color col = (Color) {lights[i].color[0] * 255, lights[i].color[1] * 255, lights[i].color[2] * 255, | ||
| lights[i].color[3] * 255}; | ||
| if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, col); | ||
| else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(col, 0.3f)); | ||
| } | ||
| EndMode3D(); | ||
|
|
||
| DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, GRAY); | ||
| DrawFPS(10, 10); | ||
|
|
||
| EndDrawing(); | ||
| //---------------------------------------------------------------------------------- | ||
| } | ||
|
|
||
| //-------------------------------------------------------------------------------------- | ||
| // De-Initialization | ||
| //-------------------------------------------------------------------------------------- | ||
|
|
||
| UnloadModel(floor.model); // Unload model | ||
| UnloadModel(model.model); // Unload model | ||
| UnloadShader(shader); // Unload Shader | ||
| UnloadPBRMaterial(floor_mat); // Unload PBRMaterial | ||
| UnloadPBRMaterial(model_mat); // Unload PBRMaterial | ||
|
|
||
| CloseWindow(); // Close window and OpenGL context | ||
| //-------------------------------------------------------------------------------------- | ||
|
|
||
| return 0; | ||
| } | ||
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| #version 100 | ||
|
|
||
| precision mediump float; | ||
|
|
||
| #define MAX_LIGHTS 4 | ||
| #define LIGHT_DIRECTIONAL 0 | ||
| #define LIGHT_POINT 1 | ||
| #define PI 3.14159265358979323846 | ||
|
|
||
| struct Light { | ||
| int enabled; | ||
| int type; | ||
| vec3 position; | ||
| vec3 target; | ||
| vec4 color; | ||
| float intensity; | ||
| }; | ||
|
|
||
| // Input vertex attributes (from vertex shader) | ||
| varying in vec3 fragPosition; | ||
| varying in vec2 fragTexCoord; | ||
| varying in vec4 fragColor; | ||
| varying in vec3 fragNormal; | ||
| varying in vec4 shadowPos; | ||
| varying in mat3 TBN; | ||
|
|
||
|
|
||
| // Input uniform values | ||
| uniform int numOfLights; | ||
| uniform sampler2D albedoMap; | ||
| uniform sampler2D mraMap; | ||
| uniform sampler2D normalMap; | ||
| uniform sampler2D emissiveMap; // r: Hight g:emissive | ||
|
|
||
| uniform vec2 tiling; | ||
| uniform vec2 offset; | ||
|
|
||
| uniform int useTexAlbedo; | ||
| uniform int useTexNormal; | ||
| uniform int useTexMRA; | ||
| uniform int useTexEmissive; | ||
|
|
||
| uniform vec4 albedoColor; | ||
| uniform vec4 emissiveColor; | ||
| uniform float normalValue; | ||
| uniform float metallicValue; | ||
| uniform float roughnessValue; | ||
| uniform float aoValue; | ||
| uniform float emissivePower; | ||
|
|
||
| // Input lighting values | ||
| uniform Light lights[MAX_LIGHTS]; | ||
| uniform vec3 viewPos; | ||
|
|
||
| uniform vec3 ambientColor; | ||
| uniform float ambient; | ||
|
|
||
| // refl in range 0 to 1 | ||
| // returns base reflectivity to 1 | ||
| // incrase reflectivity when surface view at larger angle | ||
| vec3 schlickFresnel(float hDotV,vec3 refl) | ||
| { | ||
| return refl + (1.0 - refl) * pow(1.0 - hDotV,5.0); | ||
| } | ||
|
|
||
| float ggxDistribution(float nDotH,float roughness) | ||
| { | ||
| float a = roughness * roughness * roughness * roughness; | ||
| float d = nDotH * nDotH * (a - 1.0) + 1.0; | ||
| d = PI * d * d; | ||
| return a / max(d,0.0000001); | ||
| } | ||
|
|
||
| float geomSmith(float nDotV,float nDotL,float roughness) | ||
| { | ||
| float r = roughness + 1.0; | ||
| float k = r * r / 8.0; | ||
| float ik = 1.0 - k; | ||
| float ggx1 = nDotV / (nDotV * ik + k); | ||
| float ggx2 = nDotL / (nDotL * ik + k); | ||
| return ggx1 * ggx2; | ||
| } | ||
|
|
||
| vec3 pbr(){ | ||
| vec3 albedo = texture2D(albedoMap,vec2(fragTexCoord.x*tiling.x+offset.x,fragTexCoord.y*tiling.y+offset.y)).rgb; | ||
| albedo = vec3(albedoColor.x*albedo.x,albedoColor.y*albedo.y,albedoColor.z*albedo.z); | ||
| float metallic = clamp(metallicValue,0.0,1.0); | ||
| float roughness = clamp(roughnessValue,0.0,1.0); | ||
| float ao = clamp(aoValue,0.0,1.0); | ||
| if(useTexMRA == 1) { | ||
| vec4 mra = texture2D(mraMap, vec2(fragTexCoord.x * tiling.x + offset.x, fragTexCoord.y * tiling.y + offset.y)); | ||
| metallic = clamp(mra.r+metallicValue,0.04,1.0); | ||
| roughness = clamp(mra.g+roughnessValue,0.04,1.0); | ||
| ao = (mra.b+aoValue)*0.5; | ||
| } | ||
|
|
||
|
|
||
|
|
||
| vec3 N = normalize(fragNormal); | ||
| if(useTexNormal == 1) { | ||
| N = texture2D(normalMap, vec2(fragTexCoord.x * tiling.x + offset.y, fragTexCoord.y * tiling.y + offset.y)).rgb; | ||
| N = normalize(N * 2.0 - 1.0); | ||
| N = normalize(N * TBN); | ||
| } | ||
|
|
||
| vec3 V = normalize(viewPos - fragPosition); | ||
|
|
||
| vec3 e = vec3(0); | ||
| e = (texture2D(emissiveMap, vec2(fragTexCoord.x*tiling.x+offset.x, fragTexCoord.y*tiling.y+offset.y)).rgb).g * emissiveColor.rgb*emissivePower * float(useTexEmissive); | ||
|
|
||
| //return N;//vec3(metallic,metallic,metallic); | ||
| //if dia-electric use base reflectivity of 0.04 otherwise ut is a metal use albedo as base reflectivity | ||
| vec3 baseRefl = mix(vec3(0.04),albedo.rgb,metallic); | ||
| vec3 Lo = vec3(0.0); // acumulate lighting lum | ||
|
|
||
| for(int i=0;i<numOfLights;++i){ | ||
|
|
||
| vec3 L = normalize(lights[i].position - fragPosition); // calc light vector | ||
| vec3 H = normalize(V + L); // calc halfway bisecting vector | ||
| float dist = length(lights[i].position - fragPosition); // calc distance to light | ||
| float attenuation = 1.0 / (dist * dist * 0.23); // calc attenuation | ||
| vec3 radiance = lights[i].color.rgb * lights[i].intensity * attenuation; // calc input radiance,light energy comming in | ||
|
|
||
| //Cook-Torrance BRDF distribution function | ||
| float nDotV = max(dot(N,V),0.0000001); | ||
| float nDotL = max(dot(N,L),0.0000001); | ||
| float hDotV = max(dot(H,V),0.0); | ||
| float nDotH = max(dot(N,H),0.0); | ||
| float D = ggxDistribution(nDotH,roughness); // larger the more micro-facets aligned to H | ||
| float G = geomSmith(nDotV,nDotL,roughness); // smaller the more micro-facets shadow | ||
| vec3 F = schlickFresnel(hDotV, baseRefl); // fresnel proportion of specular reflectance | ||
|
|
||
| vec3 spec = (D * G * F) / (4.0 * nDotV * nDotL); | ||
| // difuse and spec light can't be above 1.0 | ||
| // kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent | ||
| vec3 kD = vec3(1.0) - F; | ||
| //mult kD by the inverse of metallnes , only non-metals should have diffuse light | ||
| kD *= 1.0 - metallic; | ||
| Lo += ((kD * albedo.rgb / PI + spec) * radiance * nDotL)*float(lights[i].enabled); // angle of light has impact on result | ||
| } | ||
| vec3 ambient_final = (ambientColor + albedo)* ambient * 0.5; | ||
| return ambient_final+Lo*ao+e; | ||
| } | ||
|
|
||
| void main() | ||
| { | ||
| vec3 color = pbr(); | ||
|
|
||
| //HDR tonemapping | ||
| color = pow(color,color + vec3(1.0)); | ||
| //gamma correction | ||
| color = pow(color,vec3(1.0/2.2)); | ||
|
|
||
| gl_FragColor = vec4(color,1.0); | ||
|
|
||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.