REMOVED: EXAMPLE: models_material_pbr
This commit is contained in:
parent
4a1bd12e2d
commit
9a568654be
17 changed files with 2 additions and 19497 deletions
|
@ -73,7 +73,6 @@ if (${PLATFORM} MATCHES "Android")
|
||||||
list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_world_screen.c)
|
list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_world_screen.c)
|
||||||
|
|
||||||
list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_mesh_picking.c)
|
list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_mesh_picking.c)
|
||||||
list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_material_pbr.c)
|
|
||||||
list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_cubicmap.c)
|
list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_cubicmap.c)
|
||||||
list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_skybox.c)
|
list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_skybox.c)
|
||||||
list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_mesh_picking.c)
|
list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_mesh_picking.c)
|
||||||
|
|
|
@ -462,7 +462,6 @@ MODELS = \
|
||||||
models/models_cubicmap \
|
models/models_cubicmap \
|
||||||
models/models_first_person_maze \
|
models/models_first_person_maze \
|
||||||
models/models_geometric_shapes \
|
models/models_geometric_shapes \
|
||||||
models/models_material_pbr \
|
|
||||||
models/models_mesh_generation \
|
models/models_mesh_generation \
|
||||||
models/models_mesh_picking \
|
models/models_mesh_picking \
|
||||||
models/models_loading \
|
models/models_loading \
|
||||||
|
@ -471,8 +470,7 @@ MODELS = \
|
||||||
models/models_skybox \
|
models/models_skybox \
|
||||||
models/models_yaw_pitch_roll \
|
models/models_yaw_pitch_roll \
|
||||||
models/models_heightmap \
|
models/models_heightmap \
|
||||||
models/models_waving_cubes \
|
models/models_waving_cubes
|
||||||
models/models_magicavoxel_loading
|
|
||||||
|
|
||||||
SHADERS = \
|
SHADERS = \
|
||||||
shaders/shaders_model_shader \
|
shaders/shaders_model_shader \
|
||||||
|
|
|
@ -110,7 +110,7 @@ Examples using raylib models functionality, including models loading/generation
|
||||||
| 72 | [models_cubicmap](models/models_cubicmap.c) | <img src="models/models_cubicmap.png" alt="models_cubicmap" width="200"> | ray | |
|
| 72 | [models_cubicmap](models/models_cubicmap.c) | <img src="models/models_cubicmap.png" alt="models_cubicmap" width="200"> | ray | |
|
||||||
| 73 | [models_first_person_maze](models/models_first_person_maze.c) | <img src="models/models_first_person_maze.png" alt="models_first_person_maze" width="200"> | ray | |
|
| 73 | [models_first_person_maze](models/models_first_person_maze.c) | <img src="models/models_first_person_maze.png" alt="models_first_person_maze" width="200"> | ray | |
|
||||||
| 74 | [models_geometric_shapes](models/models_geometric_shapes.c) | <img src="models/models_geometric_shapes.png" alt="models_geometric_shapes" width="200"> | ray | |
|
| 74 | [models_geometric_shapes](models/models_geometric_shapes.c) | <img src="models/models_geometric_shapes.png" alt="models_geometric_shapes" width="200"> | ray | |
|
||||||
| 75 | [models_material_pbr](models/models_material_pbr.c) | <img src="models/models_material_pbr.png" alt="models_material_pbr" width="200"> | ray | |
|
| 75 | [...]() | | ray | |
|
||||||
| 76 | [models_mesh_generation](models/models_mesh_generation.c) | <img src="models/models_mesh_generation.png" alt="models_mesh_generation" width="200"> | ray | |
|
| 76 | [models_mesh_generation](models/models_mesh_generation.c) | <img src="models/models_mesh_generation.png" alt="models_mesh_generation" width="200"> | ray | |
|
||||||
| 77 | [models_mesh_picking](models/models_mesh_picking.c) | <img src="models/models_mesh_picking.png" alt="models_mesh_picking" width="200"> | [Joel Davis](https://github.com/joeld42) | |
|
| 77 | [models_mesh_picking](models/models_mesh_picking.c) | <img src="models/models_mesh_picking.png" alt="models_mesh_picking" width="200"> | [Joel Davis](https://github.com/joeld42) | |
|
||||||
| 78 | [models_loading](models/models_loading.c) | <img src="models/models_loading.png" alt="models_loading" width="200"> | ray | |
|
| 78 | [models_loading](models/models_loading.c) | <img src="models/models_loading.png" alt="models_loading" width="200"> | ray | |
|
||||||
|
|
|
@ -1,543 +0,0 @@
|
||||||
/*******************************************************************************************
|
|
||||||
*
|
|
||||||
* raylib [models] example - PBR material
|
|
||||||
*
|
|
||||||
* NOTE: This example requires raylib OpenGL 3.3 for shaders support and only #version 330
|
|
||||||
* is currently supported. OpenGL ES 2.0 platforms are not supported at the moment.
|
|
||||||
*
|
|
||||||
* This example has been created using raylib 1.8 (www.raylib.com)
|
|
||||||
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2017 Ramon Santamaria (@raysan5)
|
|
||||||
*
|
|
||||||
********************************************************************************************/
|
|
||||||
|
|
||||||
#include "raylib.h"
|
|
||||||
#include "raymath.h"
|
|
||||||
#include "rlgl.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#define RLIGHTS_IMPLEMENTATION
|
|
||||||
#include "rlights.h"
|
|
||||||
|
|
||||||
#if defined(PLATFORM_DESKTOP)
|
|
||||||
#define GLSL_VERSION 330
|
|
||||||
#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB
|
|
||||||
#define GLSL_VERSION 100
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CUBEMAP_SIZE 1024 // Cubemap texture size
|
|
||||||
#define IRRADIANCE_SIZE 32 // Irradiance texture size
|
|
||||||
#define PREFILTERED_SIZE 256 // Prefiltered HDR environment texture size
|
|
||||||
#define BRDF_SIZE 512 // BRDF LUT texture size
|
|
||||||
#define LIGHT_DISTANCE 1000.0f
|
|
||||||
#define LIGHT_HEIGHT 1.0f
|
|
||||||
|
|
||||||
// PBR texture maps generation
|
|
||||||
static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int size, int format); // Generate cubemap (6 faces) from equirectangular (panorama) texture
|
|
||||||
static TextureCubemap GenTextureIrradiance(Shader shader, TextureCubemap cubemap, int size); // Generate irradiance cubemap using cubemap texture
|
|
||||||
static TextureCubemap GenTexturePrefilter(Shader shader, TextureCubemap cubemap, int size); // Generate prefilter cubemap using cubemap texture
|
|
||||||
static Texture2D GenTextureBRDF(Shader shader, int size); // Generate a generic BRDF texture
|
|
||||||
|
|
||||||
// PBR material loading
|
|
||||||
static Material LoadMaterialPBR(Color albedo, float metalness, float roughness);
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
// Initialization
|
|
||||||
//--------------------------------------------------------------------------------------
|
|
||||||
const int screenWidth = 800;
|
|
||||||
const int screenHeight = 450;
|
|
||||||
|
|
||||||
SetConfigFlags(FLAG_MSAA_4X_HINT); // Enable Multi Sampling Anti Aliasing 4x (if available)
|
|
||||||
InitWindow(screenWidth, screenHeight, "raylib [models] example - pbr material");
|
|
||||||
|
|
||||||
// Define the camera to look into our 3d world
|
|
||||||
Camera camera = { 0 };
|
|
||||||
camera.position = (Vector3){ 4.0f, 4.0f, 4.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 mode type
|
|
||||||
|
|
||||||
// Load model and PBR material
|
|
||||||
Model model = LoadModel("resources/pbr/trooper.obj");
|
|
||||||
|
|
||||||
// Mesh tangents are generated... and uploaded to GPU
|
|
||||||
// NOTE: New VBO for tangents is generated at default location and also binded to mesh VAO
|
|
||||||
//MeshTangents(&model.meshes[0]);
|
|
||||||
|
|
||||||
model.materials[0] = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f);
|
|
||||||
|
|
||||||
// Create lights
|
|
||||||
// NOTE: Lights are added to an internal lights pool automatically
|
|
||||||
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);
|
|
||||||
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);
|
|
||||||
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);
|
|
||||||
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);
|
|
||||||
|
|
||||||
SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode
|
|
||||||
|
|
||||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
|
||||||
//--------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Main game loop
|
|
||||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
|
||||||
{
|
|
||||||
// Update
|
|
||||||
//----------------------------------------------------------------------------------
|
|
||||||
UpdateCamera(&camera); // Update camera
|
|
||||||
|
|
||||||
// Send to material PBR shader camera view position
|
|
||||||
float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
|
|
||||||
SetShaderValue(model.materials[0].shader, model.materials[0].shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
|
|
||||||
//----------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Draw
|
|
||||||
//----------------------------------------------------------------------------------
|
|
||||||
BeginDrawing();
|
|
||||||
|
|
||||||
ClearBackground(RAYWHITE);
|
|
||||||
|
|
||||||
BeginMode3D(camera);
|
|
||||||
|
|
||||||
DrawModel(model, Vector3Zero(), 1.0f, WHITE);
|
|
||||||
|
|
||||||
DrawGrid(10, 1.0f);
|
|
||||||
|
|
||||||
EndMode3D();
|
|
||||||
|
|
||||||
DrawFPS(10, 10);
|
|
||||||
|
|
||||||
EndDrawing();
|
|
||||||
//----------------------------------------------------------------------------------
|
|
||||||
}
|
|
||||||
|
|
||||||
// De-Initialization
|
|
||||||
//--------------------------------------------------------------------------------------
|
|
||||||
UnloadMaterial(model.materials[0]); // Unload material: shader and textures
|
|
||||||
|
|
||||||
UnloadModel(model); // Unload model
|
|
||||||
|
|
||||||
CloseWindow(); // Close window and OpenGL context
|
|
||||||
//--------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load PBR material (Supports: ALBEDO, NORMAL, METALNESS, ROUGHNESS, AO, EMMISIVE, HEIGHT maps)
|
|
||||||
// NOTE: PBR shader is loaded inside this function
|
|
||||||
static Material LoadMaterialPBR(Color albedo, float metalness, float roughness)
|
|
||||||
{
|
|
||||||
Material mat = LoadMaterialDefault(); // Initialize material to default
|
|
||||||
|
|
||||||
// Load PBR shader (requires several maps)
|
|
||||||
mat.shader = LoadShader(TextFormat("resources/shaders/glsl%i/pbr.vs", GLSL_VERSION),
|
|
||||||
TextFormat("resources/shaders/glsl%i/pbr.fs", GLSL_VERSION));
|
|
||||||
|
|
||||||
// Get required locations points for PBR material
|
|
||||||
// NOTE: Those location names must be available and used in the shader code
|
|
||||||
mat.shader.locs[SHADER_LOC_MAP_ALBEDO] = GetShaderLocation(mat.shader, "albedo.sampler");
|
|
||||||
mat.shader.locs[SHADER_LOC_MAP_METALNESS] = GetShaderLocation(mat.shader, "metalness.sampler");
|
|
||||||
mat.shader.locs[SHADER_LOC_MAP_NORMAL] = GetShaderLocation(mat.shader, "normals.sampler");
|
|
||||||
mat.shader.locs[SHADER_LOC_MAP_ROUGHNESS] = GetShaderLocation(mat.shader, "roughness.sampler");
|
|
||||||
mat.shader.locs[SHADER_LOC_MAP_OCCLUSION] = GetShaderLocation(mat.shader, "occlusion.sampler");
|
|
||||||
//mat.shader.locs[SHADER_LOC_MAP_EMISSION] = GetShaderLocation(mat.shader, "emission.sampler");
|
|
||||||
//mat.shader.locs[SHADER_LOC_MAP_HEIGHT] = GetShaderLocation(mat.shader, "height.sampler");
|
|
||||||
mat.shader.locs[SHADER_LOC_MAP_IRRADIANCE] = GetShaderLocation(mat.shader, "irradianceMap");
|
|
||||||
mat.shader.locs[SHADER_LOC_MAP_PREFILTER] = GetShaderLocation(mat.shader, "prefilterMap");
|
|
||||||
mat.shader.locs[SHADER_LOC_MAP_BRDF] = GetShaderLocation(mat.shader, "brdfLUT");
|
|
||||||
|
|
||||||
// Set view matrix location
|
|
||||||
mat.shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(mat.shader, "matModel");
|
|
||||||
//mat.shader.locs[SHADER_LOC_MATRIX_VIEW] = GetShaderLocation(mat.shader, "view");
|
|
||||||
mat.shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(mat.shader, "viewPos");
|
|
||||||
|
|
||||||
// Set PBR standard maps
|
|
||||||
mat.maps[MATERIAL_MAP_ALBEDO].texture = LoadTexture("resources/pbr/trooper_albedo.png");
|
|
||||||
mat.maps[MATERIAL_MAP_NORMAL].texture = LoadTexture("resources/pbr/trooper_normals.png");
|
|
||||||
mat.maps[MATERIAL_MAP_METALNESS].texture = LoadTexture("resources/pbr/trooper_metalness.png");
|
|
||||||
mat.maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTexture("resources/pbr/trooper_roughness.png");
|
|
||||||
mat.maps[MATERIAL_MAP_OCCLUSION].texture = LoadTexture("resources/pbr/trooper_ao.png");
|
|
||||||
|
|
||||||
// Set textures filtering for better quality
|
|
||||||
SetTextureFilter(mat.maps[MATERIAL_MAP_ALBEDO].texture, TEXTURE_FILTER_BILINEAR);
|
|
||||||
SetTextureFilter(mat.maps[MATERIAL_MAP_NORMAL].texture, TEXTURE_FILTER_BILINEAR);
|
|
||||||
SetTextureFilter(mat.maps[MATERIAL_MAP_METALNESS].texture, TEXTURE_FILTER_BILINEAR);
|
|
||||||
SetTextureFilter(mat.maps[MATERIAL_MAP_ROUGHNESS].texture, TEXTURE_FILTER_BILINEAR);
|
|
||||||
SetTextureFilter(mat.maps[MATERIAL_MAP_OCCLUSION].texture, TEXTURE_FILTER_BILINEAR);
|
|
||||||
|
|
||||||
// Enable sample usage in shader for assigned textures
|
|
||||||
SetShaderValue(mat.shader, GetShaderLocation(mat.shader, "albedo.useSampler"), (int[1]){ 1 }, SHADER_UNIFORM_INT);
|
|
||||||
SetShaderValue(mat.shader, GetShaderLocation(mat.shader, "normals.useSampler"), (int[1]){ 1 }, SHADER_UNIFORM_INT);
|
|
||||||
SetShaderValue(mat.shader, GetShaderLocation(mat.shader, "metalness.useSampler"), (int[1]){ 1 }, SHADER_UNIFORM_INT);
|
|
||||||
SetShaderValue(mat.shader, GetShaderLocation(mat.shader, "roughness.useSampler"), (int[1]){ 1 }, SHADER_UNIFORM_INT);
|
|
||||||
SetShaderValue(mat.shader, GetShaderLocation(mat.shader, "occlusion.useSampler"), (int[1]){ 1 }, SHADER_UNIFORM_INT);
|
|
||||||
|
|
||||||
int renderModeLoc = GetShaderLocation(mat.shader, "renderMode");
|
|
||||||
SetShaderValue(mat.shader, renderModeLoc, (int[1]){ 0 }, SHADER_UNIFORM_INT);
|
|
||||||
|
|
||||||
// Set up material properties color
|
|
||||||
mat.maps[MATERIAL_MAP_ALBEDO].color = albedo;
|
|
||||||
mat.maps[MATERIAL_MAP_NORMAL].color = (Color){ 128, 128, 255, 255 };
|
|
||||||
mat.maps[MATERIAL_MAP_METALNESS].value = metalness;
|
|
||||||
mat.maps[MATERIAL_MAP_ROUGHNESS].value = roughness;
|
|
||||||
mat.maps[MATERIAL_MAP_OCCLUSION].value = 1.0f;
|
|
||||||
mat.maps[MATERIAL_MAP_EMISSION].value = 0.5f;
|
|
||||||
mat.maps[MATERIAL_MAP_HEIGHT].value = 0.5f;
|
|
||||||
|
|
||||||
// Generate cubemap from panorama texture
|
|
||||||
//--------------------------------------------------------------------------------------------------------
|
|
||||||
Texture2D panorama = LoadTexture("resources/dresden_square_2k.hdr");
|
|
||||||
|
|
||||||
// Load equirectangular to cubemap shader
|
|
||||||
Shader shdrCubemap = LoadShader(TextFormat("resources/shaders/glsl%i/pbr.vs", GLSL_VERSION),
|
|
||||||
TextFormat("resources/shaders/glsl%i/pbr.fs", GLSL_VERSION));
|
|
||||||
|
|
||||||
SetShaderValue(shdrCubemap, GetShaderLocation(shdrCubemap, "equirectangularMap"), (int[1]){ 0 }, SHADER_UNIFORM_INT);
|
|
||||||
TextureCubemap cubemap = GenTextureCubemap(shdrCubemap, panorama, CUBEMAP_SIZE, PIXELFORMAT_UNCOMPRESSED_R32G32B32);
|
|
||||||
UnloadTexture(panorama);
|
|
||||||
UnloadShader(shdrCubemap);
|
|
||||||
//--------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Generate irradiance map from cubemap texture
|
|
||||||
//--------------------------------------------------------------------------------------------------------
|
|
||||||
// Load irradiance (GI) calculation shader
|
|
||||||
Shader shdrIrradiance = LoadShader(TextFormat("resources/shaders/glsl%i/skybox.vs", GLSL_VERSION),
|
|
||||||
TextFormat("resources/shaders/glsl%i/irradiance.fs", GLSL_VERSION));
|
|
||||||
|
|
||||||
SetShaderValue(shdrIrradiance, GetShaderLocation(shdrIrradiance, "environmentMap"), (int[1]){ 0 }, SHADER_UNIFORM_INT);
|
|
||||||
mat.maps[MATERIAL_MAP_IRRADIANCE].texture = GenTextureIrradiance(shdrIrradiance, cubemap, IRRADIANCE_SIZE);
|
|
||||||
UnloadShader(shdrIrradiance);
|
|
||||||
//--------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Generate prefilter map from cubemap texture
|
|
||||||
//--------------------------------------------------------------------------------------------------------
|
|
||||||
// Load reflection prefilter calculation shader
|
|
||||||
Shader shdrPrefilter = LoadShader(TextFormat("resources/shaders/glsl%i/skybox.vs", GLSL_VERSION),
|
|
||||||
TextFormat("resources/shaders/glsl%i/prefilter.fs", GLSL_VERSION));
|
|
||||||
|
|
||||||
SetShaderValue(shdrPrefilter, GetShaderLocation(shdrPrefilter, "environmentMap"), (int[1]){ 0 }, SHADER_UNIFORM_INT);
|
|
||||||
mat.maps[MATERIAL_MAP_PREFILTER].texture = GenTexturePrefilter(shdrPrefilter, cubemap, PREFILTERED_SIZE);
|
|
||||||
UnloadTexture(cubemap);
|
|
||||||
UnloadShader(shdrPrefilter);
|
|
||||||
//--------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Generate BRDF (bidirectional reflectance distribution function) texture (using shader)
|
|
||||||
//--------------------------------------------------------------------------------------------------------
|
|
||||||
Shader shdrBRDF = LoadShader(TextFormat("resources/shaders/glsl%i/brdf.vs", GLSL_VERSION),
|
|
||||||
TextFormat("resources/shaders/glsl%i/brdf.fs", GLSL_VERSION));
|
|
||||||
|
|
||||||
mat.maps[MATERIAL_MAP_BRDF].texture = GenTextureBRDF(shdrBRDF, BRDF_SIZE);
|
|
||||||
UnloadShader(shdrBRDF);
|
|
||||||
//--------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
return mat;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Texture maps generation (PBR)
|
|
||||||
//-------------------------------------------------------------------------------------------
|
|
||||||
// Generate cubemap texture from HDR texture
|
|
||||||
static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int size, int format)
|
|
||||||
{
|
|
||||||
TextureCubemap cubemap = { 0 };
|
|
||||||
|
|
||||||
rlDisableBackfaceCulling(); // Disable backface culling to render inside the cube
|
|
||||||
|
|
||||||
// STEP 1: Setup framebuffer
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
unsigned int rbo = rlLoadTextureDepth(size, size, true);
|
|
||||||
cubemap.id = rlLoadTextureCubemap(NULL, size, format);
|
|
||||||
|
|
||||||
unsigned int fbo = rlLoadFramebuffer(size, size);
|
|
||||||
rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
|
|
||||||
rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X, 0);
|
|
||||||
|
|
||||||
// Check if framebuffer is complete with attachments (valid)
|
|
||||||
if (rlFramebufferComplete(fbo)) TraceLog(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", fbo);
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// STEP 2: Draw to framebuffer
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: Shader is used to convert HDR equirectangular environment map to cubemap equivalent (6 faces)
|
|
||||||
rlEnableShader(shader.id);
|
|
||||||
|
|
||||||
// Define projection matrix and send it to shader
|
|
||||||
Matrix matFboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
|
|
||||||
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_PROJECTION], matFboProjection);
|
|
||||||
|
|
||||||
// Define view matrix for every side of the cubemap
|
|
||||||
Matrix fboViews[6] = {
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f })
|
|
||||||
};
|
|
||||||
|
|
||||||
rlViewport(0, 0, size, size); // Set viewport to current fbo dimensions
|
|
||||||
|
|
||||||
// Activate and enable texture for drawing to cubemap faces
|
|
||||||
rlActiveTextureSlot(0);
|
|
||||||
rlEnableTexture(panorama.id);
|
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
// Set the view matrix for the current cube face
|
|
||||||
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]);
|
|
||||||
|
|
||||||
// Select the current cubemap face attachment for the fbo
|
|
||||||
// WARNING: This function by default enables->attach->disables fbo!!!
|
|
||||||
rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i, 0);
|
|
||||||
rlEnableFramebuffer(fbo);
|
|
||||||
|
|
||||||
// Load and draw a cube, it uses the current enabled texture
|
|
||||||
rlClearScreenBuffers();
|
|
||||||
rlLoadDrawCube();
|
|
||||||
|
|
||||||
// ALTERNATIVE: Try to use internal batch system to draw the cube instead of rlLoadDrawCube
|
|
||||||
// for some reason this method does not work, maybe due to cube triangles definition? normals pointing out?
|
|
||||||
// TODO: Investigate this issue...
|
|
||||||
//rlSetTexture(panorama.id); // WARNING: It must be called after enabling current framebuffer if using internal batch system!
|
|
||||||
//rlClearScreenBuffers();
|
|
||||||
//DrawCubeV(Vector3Zero(), Vector3One(), WHITE);
|
|
||||||
//rlDrawRenderBatchActive();
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// STEP 3: Unload framebuffer and reset state
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
rlDisableShader(); // Unbind shader
|
|
||||||
rlDisableTexture(); // Unbind texture
|
|
||||||
rlDisableFramebuffer(); // Unbind framebuffer
|
|
||||||
rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer)
|
|
||||||
|
|
||||||
// Reset viewport dimensions to default
|
|
||||||
rlViewport(0, 0, rlGetFramebufferWidth(), rlGetFramebufferHeight());
|
|
||||||
rlEnableBackfaceCulling();
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
cubemap.width = size;
|
|
||||||
cubemap.height = size;
|
|
||||||
cubemap.mipmaps = 1;
|
|
||||||
cubemap.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32;
|
|
||||||
|
|
||||||
return cubemap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate irradiance texture using cubemap data
|
|
||||||
static TextureCubemap GenTextureIrradiance(Shader shader, TextureCubemap cubemap, int size)
|
|
||||||
{
|
|
||||||
TextureCubemap irradiance = { 0 };
|
|
||||||
|
|
||||||
rlDisableBackfaceCulling(); // Disable backface culling to render inside the cube
|
|
||||||
|
|
||||||
// STEP 1: Setup framebuffer
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
unsigned int rbo = rlLoadTextureDepth(size, size, true);
|
|
||||||
irradiance.id = rlLoadTextureCubemap(NULL, size, PIXELFORMAT_UNCOMPRESSED_R32G32B32);
|
|
||||||
|
|
||||||
unsigned int fbo = rlLoadFramebuffer(size, size);
|
|
||||||
rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
|
|
||||||
rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X, 0);
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// STEP 2: Draw to framebuffer
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: Shader is used to solve diffuse integral by convolution to create an irradiance cubemap
|
|
||||||
rlEnableShader(shader.id);
|
|
||||||
|
|
||||||
// Define projection matrix and send it to shader
|
|
||||||
Matrix matFboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
|
|
||||||
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_PROJECTION], matFboProjection);
|
|
||||||
|
|
||||||
// Define view matrix for every side of the cubemap
|
|
||||||
Matrix fboViews[6] = {
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f })
|
|
||||||
};
|
|
||||||
|
|
||||||
rlActiveTextureSlot(0);
|
|
||||||
rlEnableTextureCubemap(cubemap.id);
|
|
||||||
|
|
||||||
rlViewport(0, 0, size, size); // Set viewport to current fbo dimensions
|
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]);
|
|
||||||
rlFramebufferAttach(fbo, irradiance.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i, 0);
|
|
||||||
|
|
||||||
rlEnableFramebuffer(fbo);
|
|
||||||
rlClearScreenBuffers();
|
|
||||||
rlLoadDrawCube();
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// STEP 3: Unload framebuffer and reset state
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
rlDisableShader(); // Unbind shader
|
|
||||||
rlDisableTexture(); // Unbind texture
|
|
||||||
rlDisableFramebuffer(); // Unbind framebuffer
|
|
||||||
rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer)
|
|
||||||
|
|
||||||
// Reset viewport dimensions to default
|
|
||||||
rlViewport(0, 0, rlGetFramebufferWidth(), rlGetFramebufferHeight());
|
|
||||||
rlEnableBackfaceCulling();
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
irradiance.width = size;
|
|
||||||
irradiance.height = size;
|
|
||||||
irradiance.mipmaps = 1;
|
|
||||||
irradiance.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32;
|
|
||||||
|
|
||||||
return irradiance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate prefilter texture using cubemap data
|
|
||||||
static TextureCubemap GenTexturePrefilter(Shader shader, TextureCubemap cubemap, int size)
|
|
||||||
{
|
|
||||||
TextureCubemap prefilter = { 0 };
|
|
||||||
|
|
||||||
rlDisableBackfaceCulling(); // Disable backface culling to render inside the cube
|
|
||||||
|
|
||||||
// STEP 1: Setup framebuffer
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
unsigned int rbo = rlLoadTextureDepth(size, size, true);
|
|
||||||
prefilter.id = rlLoadTextureCubemap(NULL, size, PIXELFORMAT_UNCOMPRESSED_R32G32B32);
|
|
||||||
rlTextureParameters(prefilter.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_MIP_LINEAR);
|
|
||||||
|
|
||||||
unsigned int fbo = rlLoadFramebuffer(size, size);
|
|
||||||
rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
|
|
||||||
rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X, 0);
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Generate mipmaps for the prefiltered HDR texture
|
|
||||||
//glGenerateMipmap(GL_TEXTURE_CUBE_MAP); // TODO!
|
|
||||||
|
|
||||||
// STEP 2: Draw to framebuffer
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: Shader is used to prefilter HDR and store data into mipmap levels
|
|
||||||
|
|
||||||
// Define projection matrix and send it to shader
|
|
||||||
Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
|
|
||||||
rlEnableShader(shader.id);
|
|
||||||
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_PROJECTION], fboProjection);
|
|
||||||
|
|
||||||
// Define view matrix for every side of the cubemap
|
|
||||||
Matrix fboViews[6] = {
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }),
|
|
||||||
MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f })
|
|
||||||
};
|
|
||||||
|
|
||||||
rlActiveTextureSlot(0);
|
|
||||||
rlEnableTextureCubemap(cubemap.id);
|
|
||||||
|
|
||||||
// TODO: Locations should be taken out of this function... too shader dependant...
|
|
||||||
int roughnessLoc = rlGetLocationUniform(shader.id, "roughness");
|
|
||||||
|
|
||||||
rlEnableFramebuffer(fbo);
|
|
||||||
|
|
||||||
#define MAX_MIPMAP_LEVELS 5 // Max number of prefilter texture mipmaps
|
|
||||||
|
|
||||||
for (int mip = 0; mip < MAX_MIPMAP_LEVELS; mip++)
|
|
||||||
{
|
|
||||||
// Resize framebuffer according to mip-level size.
|
|
||||||
unsigned int mipWidth = size*(int)powf(0.5f, (float)mip);
|
|
||||||
unsigned int mipHeight = size*(int)powf(0.5f, (float)mip);
|
|
||||||
|
|
||||||
rlViewport(0, 0, mipWidth, mipHeight);
|
|
||||||
|
|
||||||
//glBindRenderbuffer(GL_RENDERBUFFER, rbo);
|
|
||||||
//glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight);
|
|
||||||
|
|
||||||
float roughness = (float)mip/(float)(MAX_MIPMAP_LEVELS - 1);
|
|
||||||
rlSetUniform(roughnessLoc, &roughness, SHADER_UNIFORM_FLOAT, 1);
|
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]);
|
|
||||||
rlFramebufferAttach(fbo, prefilter.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i, mip);
|
|
||||||
|
|
||||||
rlClearScreenBuffers();
|
|
||||||
rlLoadDrawCube();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// STEP 3: Unload framebuffer and reset state
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
rlDisableShader(); // Unbind shader
|
|
||||||
rlDisableTexture(); // Unbind texture
|
|
||||||
rlDisableFramebuffer(); // Unbind framebuffer
|
|
||||||
rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer)
|
|
||||||
|
|
||||||
// Reset viewport dimensions to default
|
|
||||||
rlViewport(0, 0, rlGetFramebufferWidth(), rlGetFramebufferHeight());
|
|
||||||
rlEnableBackfaceCulling();
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
prefilter.width = size;
|
|
||||||
prefilter.height = size;
|
|
||||||
prefilter.mipmaps = MAX_MIPMAP_LEVELS;
|
|
||||||
prefilter.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32;
|
|
||||||
|
|
||||||
return prefilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate BRDF texture using cubemap data
|
|
||||||
// TODO: Review implementation: https://github.com/HectorMF/BRDFGenerator
|
|
||||||
static Texture2D GenTextureBRDF(Shader shader, int size)
|
|
||||||
{
|
|
||||||
Texture2D brdf = { 0 };
|
|
||||||
|
|
||||||
// STEP 1: Setup framebuffer
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
unsigned int rbo = rlLoadTextureDepth(size, size, true);
|
|
||||||
brdf.id = rlLoadTexture(NULL, size, size, PIXELFORMAT_UNCOMPRESSED_R32G32B32, 1);
|
|
||||||
|
|
||||||
unsigned int fbo = rlLoadFramebuffer(size, size);
|
|
||||||
rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
|
|
||||||
rlFramebufferAttach(fbo, brdf.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// STEP 2: Draw to framebuffer
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: Render BRDF LUT into a quad using FBO
|
|
||||||
rlEnableShader(shader.id);
|
|
||||||
|
|
||||||
rlViewport(0, 0, size, size);
|
|
||||||
|
|
||||||
rlEnableFramebuffer(fbo);
|
|
||||||
rlClearScreenBuffers();
|
|
||||||
|
|
||||||
rlLoadDrawQuad();
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// STEP 3: Unload framebuffer and reset state
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
rlDisableShader(); // Unbind shader
|
|
||||||
rlDisableTexture(); // Unbind texture
|
|
||||||
rlDisableFramebuffer(); // Unbind framebuffer
|
|
||||||
rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer)
|
|
||||||
|
|
||||||
// Reset viewport dimensions to default
|
|
||||||
rlViewport(0, 0, rlGetFramebufferWidth(), rlGetFramebufferHeight());
|
|
||||||
//------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
brdf.width = size;
|
|
||||||
brdf.height = size;
|
|
||||||
brdf.mipmaps = 1;
|
|
||||||
brdf.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32;
|
|
||||||
|
|
||||||
return brdf;
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 317 KiB |
File diff suppressed because it is too large
Load diff
Binary file not shown.
Before Width: | Height: | Size: 1.8 MiB |
Binary file not shown.
Before Width: | Height: | Size: 547 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
Before Width: | Height: | Size: 717 KiB |
|
@ -1,124 +0,0 @@
|
||||||
#version 330
|
|
||||||
|
|
||||||
// Input vertex attributes (from vertex shader)
|
|
||||||
in vec2 fragTexCoord;
|
|
||||||
|
|
||||||
// Constant values
|
|
||||||
const float PI = 3.14159265359;
|
|
||||||
const uint MAX_SAMPLES = 1024u;
|
|
||||||
|
|
||||||
// Output fragment color
|
|
||||||
out vec4 finalColor;
|
|
||||||
|
|
||||||
vec2 Hammersley(uint i, uint N);
|
|
||||||
float RadicalInverseVdC(uint bits);
|
|
||||||
float GeometrySchlickGGX(float NdotV, float roughness);
|
|
||||||
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness);
|
|
||||||
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness);
|
|
||||||
vec2 IntegrateBRDF(float NdotV, float roughness);
|
|
||||||
|
|
||||||
float RadicalInverseVdC(uint bits)
|
|
||||||
{
|
|
||||||
bits = (bits << 16u) | (bits >> 16u);
|
|
||||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
|
||||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
|
||||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
|
||||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
|
||||||
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute Hammersley coordinates
|
|
||||||
vec2 Hammersley(uint i, uint N)
|
|
||||||
{
|
|
||||||
return vec2(float(i)/float(N), RadicalInverseVdC(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Integrate number of importance samples for (roughness and NoV)
|
|
||||||
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)
|
|
||||||
{
|
|
||||||
float a = roughness*roughness;
|
|
||||||
float phi = 2.0 * PI * Xi.x;
|
|
||||||
float cosTheta = sqrt((1.0 - Xi.y)/(1.0 + (a*a - 1.0)*Xi.y));
|
|
||||||
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
|
|
||||||
|
|
||||||
// Transform from spherical coordinates to cartesian coordinates (halfway vector)
|
|
||||||
vec3 H = vec3(cos(phi)*sinTheta, sin(phi)*sinTheta, cosTheta);
|
|
||||||
|
|
||||||
// Transform from tangent space H vector to world space sample vector
|
|
||||||
vec3 up = ((abs(N.z) < 0.999) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0));
|
|
||||||
vec3 tangent = normalize(cross(up, N));
|
|
||||||
vec3 bitangent = cross(N, tangent);
|
|
||||||
vec3 sampleVec = tangent*H.x + bitangent*H.y + N*H.z;
|
|
||||||
|
|
||||||
return normalize(sampleVec);
|
|
||||||
}
|
|
||||||
|
|
||||||
float GeometrySchlickGGX(float NdotV, float roughness)
|
|
||||||
{
|
|
||||||
// For IBL k is calculated different
|
|
||||||
float k = (roughness*roughness)/2.0;
|
|
||||||
|
|
||||||
float nom = NdotV;
|
|
||||||
float denom = NdotV*(1.0 - k) + k;
|
|
||||||
|
|
||||||
return nom/denom;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the geometry term for the BRDF given roughness squared, NoV, NoL
|
|
||||||
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
|
|
||||||
{
|
|
||||||
float NdotV = max(dot(N, V), 0.0);
|
|
||||||
float NdotL = max(dot(N, L), 0.0);
|
|
||||||
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
|
||||||
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
|
||||||
|
|
||||||
return ggx1*ggx2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bidirectional reflectance distribution function
|
|
||||||
// Ref: https://github.com/HectorMF/BRDFGenerator
|
|
||||||
vec2 IntegrateBRDF(float NdotV, float roughness)
|
|
||||||
{
|
|
||||||
float A = 0.0;
|
|
||||||
float B = 0.0;
|
|
||||||
vec3 V = vec3(sqrt(1.0 - NdotV*NdotV), 0.0, NdotV);
|
|
||||||
vec3 N = vec3(0.0, 0.0, 1.0);
|
|
||||||
|
|
||||||
for (uint i = 0u; i < MAX_SAMPLES; i++)
|
|
||||||
{
|
|
||||||
// Generate a sample vector that's biased towards the preferred alignment direction (importance sampling)
|
|
||||||
|
|
||||||
vec2 Xi = Hammersley(i, MAX_SAMPLES); // Compute a Hammersely coordinate
|
|
||||||
vec3 H = ImportanceSampleGGX(Xi, N, roughness); // Integrate number of importance samples for (roughness and NoV)
|
|
||||||
vec3 L = normalize(2.0*dot(V, H)*H - V); // Compute reflection vector L
|
|
||||||
|
|
||||||
float NdotL = max(L.z, 0.0); // Compute normal dot light
|
|
||||||
float NdotH = max(H.z, 0.0); // Compute normal dot half
|
|
||||||
float VdotH = max(dot(V, H), 0.0); // Compute view dot half
|
|
||||||
|
|
||||||
if (NdotL > 0.0)
|
|
||||||
{
|
|
||||||
float G = GeometrySmith(N, V, L, roughness); // Compute the geometry term for the BRDF given roughness squared, NoV, NoL
|
|
||||||
float GVis = (G*VdotH)/(NdotH*NdotV); // Compute the visibility term given G, VoH, NoH, NoV, NoL
|
|
||||||
float Fc = pow(1.0 - VdotH, 5.0); // Compute the fresnel term given VoH
|
|
||||||
|
|
||||||
A += (1.0 - Fc)*GVis; // Sum the result given fresnel, geometry, visibility
|
|
||||||
B += Fc*GVis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate brdf average sample
|
|
||||||
A /= float(MAX_SAMPLES);
|
|
||||||
B /= float(MAX_SAMPLES);
|
|
||||||
|
|
||||||
return vec2(A, B);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Calculate brdf based on texture coordinates
|
|
||||||
vec2 brdf = IntegrateBRDF(fragTexCoord.x, fragTexCoord.y);
|
|
||||||
|
|
||||||
// Calculate final fragment color
|
|
||||||
finalColor = vec4(brdf.r, brdf.g, 0.0, 1.0);
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
#version 330
|
|
||||||
|
|
||||||
// Input vertex attributes
|
|
||||||
in vec3 vertexPosition;
|
|
||||||
in vec2 vertexTexCoord;
|
|
||||||
|
|
||||||
// Output vertex attributes (to fragment shader)
|
|
||||||
out vec2 fragTexCoord;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Calculate fragment position based on model transformations
|
|
||||||
fragTexCoord = vertexTexCoord;
|
|
||||||
|
|
||||||
// Calculate final vertex position
|
|
||||||
gl_Position = vec4(vertexPosition, 1.0);
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
#version 330
|
|
||||||
|
|
||||||
// Input vertex attributes (from vertex shader)
|
|
||||||
in vec3 fragPosition;
|
|
||||||
|
|
||||||
// Input uniform values
|
|
||||||
uniform samplerCube environmentMap;
|
|
||||||
|
|
||||||
// Constant values
|
|
||||||
const float PI = 3.14159265359;
|
|
||||||
|
|
||||||
// Output fragment color
|
|
||||||
out vec4 finalColor;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// The sample direction equals the hemisphere's orientation
|
|
||||||
vec3 normal = normalize(fragPosition);
|
|
||||||
|
|
||||||
vec3 irradiance = vec3(0.0);
|
|
||||||
|
|
||||||
vec3 up = vec3(0.0, 1.0, 0.0);
|
|
||||||
vec3 right = cross(up, normal);
|
|
||||||
up = cross(normal, right);
|
|
||||||
|
|
||||||
float sampleDelta = 0.025;
|
|
||||||
float nrSamples = 0.0;
|
|
||||||
|
|
||||||
for (float phi = 0.0; phi < 2.0*PI; phi += sampleDelta)
|
|
||||||
{
|
|
||||||
for (float theta = 0.0; theta < 0.5*PI; theta += sampleDelta)
|
|
||||||
{
|
|
||||||
// Spherical to cartesian (in tangent space)
|
|
||||||
vec3 tangentSample = vec3(sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta));
|
|
||||||
|
|
||||||
// tangent space to world
|
|
||||||
vec3 sampleVec = tangentSample.x*right + tangentSample.y*up + tangentSample.z*normal;
|
|
||||||
|
|
||||||
// Fetch color from environment cubemap
|
|
||||||
irradiance += texture(environmentMap, sampleVec).rgb*cos(theta)*sin(theta);
|
|
||||||
nrSamples++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate irradiance average value from samples
|
|
||||||
irradiance = PI*irradiance*(1.0/float(nrSamples));
|
|
||||||
|
|
||||||
// Calculate final fragment color
|
|
||||||
finalColor = vec4(irradiance, 1.0);
|
|
||||||
}
|
|
|
@ -1,292 +0,0 @@
|
||||||
#version 330
|
|
||||||
|
|
||||||
#define MAX_REFLECTION_LOD 4.0
|
|
||||||
#define MAX_DEPTH_LAYER 20
|
|
||||||
#define MIN_DEPTH_LAYER 10
|
|
||||||
|
|
||||||
#define MAX_LIGHTS 4
|
|
||||||
#define LIGHT_DIRECTIONAL 0
|
|
||||||
#define LIGHT_POINT 1
|
|
||||||
|
|
||||||
struct MaterialProperty {
|
|
||||||
vec3 color;
|
|
||||||
int useSampler;
|
|
||||||
sampler2D sampler;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Light {
|
|
||||||
int enabled;
|
|
||||||
int type;
|
|
||||||
vec3 position;
|
|
||||||
vec3 target;
|
|
||||||
vec4 color;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Input vertex attributes (from vertex shader)
|
|
||||||
in vec3 fragPosition;
|
|
||||||
in vec2 fragTexCoord;
|
|
||||||
in vec3 fragNormal;
|
|
||||||
in vec3 fragTangent;
|
|
||||||
in vec3 fragBinormal;
|
|
||||||
|
|
||||||
// Input material values
|
|
||||||
uniform MaterialProperty albedo;
|
|
||||||
uniform MaterialProperty normals;
|
|
||||||
uniform MaterialProperty metalness;
|
|
||||||
uniform MaterialProperty roughness;
|
|
||||||
uniform MaterialProperty occlusion;
|
|
||||||
uniform MaterialProperty emission;
|
|
||||||
uniform MaterialProperty height;
|
|
||||||
|
|
||||||
// Input uniform values
|
|
||||||
uniform samplerCube irradianceMap;
|
|
||||||
uniform samplerCube prefilterMap;
|
|
||||||
uniform sampler2D brdfLUT;
|
|
||||||
|
|
||||||
// Input lighting values
|
|
||||||
uniform Light lights[MAX_LIGHTS];
|
|
||||||
|
|
||||||
// Other uniform values
|
|
||||||
uniform int renderMode;
|
|
||||||
uniform vec3 viewPos;
|
|
||||||
vec2 texCoord;
|
|
||||||
|
|
||||||
// Constant values
|
|
||||||
const float PI = 3.14159265359;
|
|
||||||
|
|
||||||
// Output fragment color
|
|
||||||
out vec4 finalColor;
|
|
||||||
|
|
||||||
vec3 ComputeMaterialProperty(MaterialProperty property);
|
|
||||||
float DistributionGGX(vec3 N, vec3 H, float roughness);
|
|
||||||
float GeometrySchlickGGX(float NdotV, float roughness);
|
|
||||||
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness);
|
|
||||||
vec3 fresnelSchlick(float cosTheta, vec3 F0);
|
|
||||||
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness);
|
|
||||||
vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir);
|
|
||||||
|
|
||||||
// WARNING: There is some weird behaviour with this function, always returns black!
|
|
||||||
// Yes, I even tried: return texture(property.sampler, texCoord).rgb;
|
|
||||||
vec3 ComputeMaterialProperty(MaterialProperty property)
|
|
||||||
{
|
|
||||||
vec3 result = vec3(0.0, 0.0, 0.0);
|
|
||||||
|
|
||||||
if (property.useSampler == 1) result = texture(property.sampler, texCoord).rgb;
|
|
||||||
else result = property.color;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
float DistributionGGX(vec3 N, vec3 H, float roughness)
|
|
||||||
{
|
|
||||||
float a = roughness*roughness;
|
|
||||||
float a2 = a*a;
|
|
||||||
float NdotH = max(dot(N, H), 0.0);
|
|
||||||
float NdotH2 = NdotH*NdotH;
|
|
||||||
|
|
||||||
float nom = a2;
|
|
||||||
float denom = (NdotH2*(a2 - 1.0) + 1.0);
|
|
||||||
denom = PI*denom*denom;
|
|
||||||
|
|
||||||
return nom/denom;
|
|
||||||
}
|
|
||||||
|
|
||||||
float GeometrySchlickGGX(float NdotV, float roughness)
|
|
||||||
{
|
|
||||||
float r = (roughness + 1.0);
|
|
||||||
float k = r*r/8.0;
|
|
||||||
|
|
||||||
float nom = NdotV;
|
|
||||||
float denom = NdotV*(1.0 - k) + k;
|
|
||||||
|
|
||||||
return nom/denom;
|
|
||||||
}
|
|
||||||
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
|
|
||||||
{
|
|
||||||
float NdotV = max(dot(N, V), 0.0);
|
|
||||||
float NdotL = max(dot(N, L), 0.0);
|
|
||||||
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
|
||||||
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
|
||||||
|
|
||||||
return ggx1*ggx2;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 fresnelSchlick(float cosTheta, vec3 F0)
|
|
||||||
{
|
|
||||||
return F0 + (1.0 - F0)*pow(1.0 - cosTheta, 5.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness)
|
|
||||||
{
|
|
||||||
return F0 + (max(vec3(1.0 - roughness), F0) - F0)*pow(1.0 - cosTheta, 5.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir)
|
|
||||||
{
|
|
||||||
// Calculate the number of depth layers and calculate the size of each layer
|
|
||||||
float numLayers = mix(MAX_DEPTH_LAYER, MIN_DEPTH_LAYER, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));
|
|
||||||
float layerDepth = 1.0/numLayers;
|
|
||||||
|
|
||||||
// Calculate depth of current layer
|
|
||||||
float currentLayerDepth = 0.0;
|
|
||||||
|
|
||||||
// Calculate the amount to shift the texture coordinates per layer (from vector P)
|
|
||||||
// Note: height amount is stored in height material attribute color R channel (sampler use is independent)
|
|
||||||
vec2 P = viewDir.xy*height.color.r;
|
|
||||||
vec2 deltaTexCoords = P/numLayers;
|
|
||||||
|
|
||||||
// Store initial texture coordinates and depth values
|
|
||||||
vec2 currentTexCoords = texCoords;
|
|
||||||
float currentDepthMapValue = texture(height.sampler, currentTexCoords).r;
|
|
||||||
|
|
||||||
while (currentLayerDepth < currentDepthMapValue)
|
|
||||||
{
|
|
||||||
// Shift texture coordinates along direction of P
|
|
||||||
currentTexCoords -= deltaTexCoords;
|
|
||||||
|
|
||||||
// Get depth map value at current texture coordinates
|
|
||||||
currentDepthMapValue = texture(height.sampler, currentTexCoords).r;
|
|
||||||
|
|
||||||
// Get depth of next layer
|
|
||||||
currentLayerDepth += layerDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get texture coordinates before collision (reverse operations)
|
|
||||||
vec2 prevTexCoords = currentTexCoords + deltaTexCoords;
|
|
||||||
|
|
||||||
// Get depth after and before collision for linear interpolation
|
|
||||||
float afterDepth = currentDepthMapValue - currentLayerDepth;
|
|
||||||
float beforeDepth = texture(height.sampler, prevTexCoords).r - currentLayerDepth + layerDepth;
|
|
||||||
|
|
||||||
// Interpolation of texture coordinates
|
|
||||||
float weight = afterDepth/(afterDepth - beforeDepth);
|
|
||||||
vec2 finalTexCoords = prevTexCoords*weight + currentTexCoords*(1.0 - weight);
|
|
||||||
|
|
||||||
return finalTexCoords;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Calculate TBN and RM matrices
|
|
||||||
mat3 TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal));
|
|
||||||
|
|
||||||
// Calculate lighting required attributes
|
|
||||||
vec3 normal = normalize(fragNormal);
|
|
||||||
vec3 view = normalize(viewPos - fragPosition);
|
|
||||||
vec3 refl = reflect(-view, normal);
|
|
||||||
|
|
||||||
// Check if parallax mapping is enabled and calculate texture coordinates to use based on height map
|
|
||||||
// NOTE: remember that 'texCoord' variable must be assigned before calling any ComputeMaterialProperty() function
|
|
||||||
if (height.useSampler == 1) texCoord = ParallaxMapping(fragTexCoord, view);
|
|
||||||
else texCoord = fragTexCoord; // Use default texture coordinates
|
|
||||||
|
|
||||||
// Fetch material values from texture sampler or color attributes
|
|
||||||
vec3 color = texture(albedo.sampler, texCoord).rgb; //ComputeMaterialProperty(albedo);
|
|
||||||
vec3 metal = texture(metalness.sampler, texCoord).rgb; //ComputeMaterialProperty(metalness);
|
|
||||||
vec3 rough = texture(roughness.sampler, texCoord).rgb; //ComputeMaterialProperty(roughness);
|
|
||||||
vec3 emiss = texture(emission.sampler, texCoord).rgb; //ComputeMaterialProperty(emission);
|
|
||||||
vec3 ao = texture(occlusion.sampler, texCoord).rgb; //ComputeMaterialProperty(occlusion);
|
|
||||||
|
|
||||||
// Check if normal mapping is enabled
|
|
||||||
if (normals.useSampler == 1)
|
|
||||||
{
|
|
||||||
// Fetch normal map color and transform lighting values to tangent space
|
|
||||||
normal = texture(normals.sampler, texCoord).rgb; //ComputeMaterialProperty(normals);
|
|
||||||
normal = normalize(normal*2.0 - 1.0);
|
|
||||||
normal = normalize(normal*TBN);
|
|
||||||
|
|
||||||
// Convert tangent space normal to world space due to cubemap reflection calculations
|
|
||||||
refl = normalize(reflect(-view, normal));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate reflectance at normal incidence
|
|
||||||
vec3 F0 = vec3(0.04);
|
|
||||||
F0 = mix(F0, color, metal.r);
|
|
||||||
|
|
||||||
// Calculate lighting for all lights
|
|
||||||
vec3 Lo = vec3(0.0);
|
|
||||||
vec3 lightDot = vec3(0.0);
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_LIGHTS; i++)
|
|
||||||
{
|
|
||||||
if (lights[i].enabled == 1)
|
|
||||||
{
|
|
||||||
// Calculate per-light radiance
|
|
||||||
vec3 light = vec3(0.0);
|
|
||||||
vec3 radiance = lights[i].color.rgb;
|
|
||||||
if (lights[i].type == LIGHT_DIRECTIONAL) light = -normalize(lights[i].target - lights[i].position);
|
|
||||||
else if (lights[i].type == LIGHT_POINT)
|
|
||||||
{
|
|
||||||
light = normalize(lights[i].position - fragPosition);
|
|
||||||
float distance = length(lights[i].position - fragPosition);
|
|
||||||
float attenuation = 1.0/(distance*distance);
|
|
||||||
radiance *= attenuation;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cook-torrance BRDF
|
|
||||||
vec3 high = normalize(view + light);
|
|
||||||
float NDF = DistributionGGX(normal, high, rough.r);
|
|
||||||
float G = GeometrySmith(normal, view, light, rough.r);
|
|
||||||
vec3 F = fresnelSchlick(max(dot(high, view), 0.0), F0);
|
|
||||||
vec3 nominator = NDF*G*F;
|
|
||||||
float denominator = 4*max(dot(normal, view), 0.0)*max(dot(normal, light), 0.0) + 0.001;
|
|
||||||
vec3 brdf = nominator/denominator;
|
|
||||||
|
|
||||||
// Store to kS the fresnel value and calculate energy conservation
|
|
||||||
vec3 kS = F;
|
|
||||||
vec3 kD = vec3(1.0) - kS;
|
|
||||||
|
|
||||||
// Multiply kD by the inverse metalness such that only non-metals have diffuse lighting
|
|
||||||
kD *= 1.0 - metal.r;
|
|
||||||
|
|
||||||
// Scale light by dot product between normal and light direction
|
|
||||||
float NdotL = max(dot(normal, light), 0.0);
|
|
||||||
|
|
||||||
// Add to outgoing radiance Lo
|
|
||||||
// Note: BRDF is already multiplied by the Fresnel so it doesn't need to be multiplied again
|
|
||||||
Lo += (kD*color/PI + brdf)*radiance*NdotL*lights[i].color.a;
|
|
||||||
lightDot += radiance*NdotL + brdf*lights[i].color.a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate ambient lighting using IBL
|
|
||||||
vec3 F = fresnelSchlickRoughness(max(dot(normal, view), 0.0), F0, rough.r);
|
|
||||||
vec3 kS = F;
|
|
||||||
vec3 kD = 1.0 - kS;
|
|
||||||
kD *= 1.0 - metal.r;
|
|
||||||
|
|
||||||
// Calculate indirect diffuse
|
|
||||||
vec3 irradiance = texture(irradianceMap, fragNormal).rgb;
|
|
||||||
vec3 diffuse = color*irradiance;
|
|
||||||
|
|
||||||
// Sample both the prefilter map and the BRDF lut and combine them together as per the Split-Sum approximation
|
|
||||||
vec3 prefilterColor = textureLod(prefilterMap, refl, rough.r*MAX_REFLECTION_LOD).rgb;
|
|
||||||
vec2 brdf = texture(brdfLUT, vec2(max(dot(normal, view), 0.0), rough.r)).rg;
|
|
||||||
vec3 reflection = prefilterColor*(F*brdf.x + brdf.y);
|
|
||||||
|
|
||||||
// Calculate final lighting
|
|
||||||
vec3 ambient = (kD*diffuse + reflection)*ao;
|
|
||||||
|
|
||||||
// Calculate fragment color based on render mode
|
|
||||||
vec3 fragmentColor = ambient + Lo + emiss; // Physically Based Rendering
|
|
||||||
|
|
||||||
if (renderMode == 1) fragmentColor = color; // Albedo
|
|
||||||
else if (renderMode == 2) fragmentColor = normal; // Normals
|
|
||||||
else if (renderMode == 3) fragmentColor = metal; // Metalness
|
|
||||||
else if (renderMode == 4) fragmentColor = rough; // Roughness
|
|
||||||
else if (renderMode == 5) fragmentColor = ao; // Ambient Occlusion
|
|
||||||
else if (renderMode == 6) fragmentColor = emiss; // Emission
|
|
||||||
else if (renderMode == 7) fragmentColor = lightDot; // Lighting
|
|
||||||
else if (renderMode == 8) fragmentColor = kS; // Fresnel
|
|
||||||
else if (renderMode == 9) fragmentColor = irradiance; // Irradiance
|
|
||||||
else if (renderMode == 10) fragmentColor = reflection; // Reflection
|
|
||||||
|
|
||||||
// Apply HDR tonemapping
|
|
||||||
fragmentColor = fragmentColor/(fragmentColor + vec3(1.0));
|
|
||||||
|
|
||||||
// Apply gamma correction
|
|
||||||
fragmentColor = pow(fragmentColor, vec3(1.0/2.2));
|
|
||||||
|
|
||||||
// Calculate final fragment color
|
|
||||||
finalColor = vec4(fragmentColor, 1.0);
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
#version 330
|
|
||||||
|
|
||||||
// Input vertex attributes
|
|
||||||
in vec3 vertexPosition;
|
|
||||||
in vec2 vertexTexCoord;
|
|
||||||
in vec3 vertexNormal;
|
|
||||||
in vec4 vertexTangent;
|
|
||||||
|
|
||||||
// Input uniform values
|
|
||||||
uniform mat4 mvp;
|
|
||||||
uniform mat4 matModel;
|
|
||||||
|
|
||||||
// Output vertex attributes (to fragment shader)
|
|
||||||
out vec3 fragPosition;
|
|
||||||
out vec2 fragTexCoord;
|
|
||||||
out vec3 fragNormal;
|
|
||||||
out vec3 fragTangent;
|
|
||||||
out vec3 fragBinormal;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Calculate binormal from vertex normal and tangent
|
|
||||||
vec3 vertexBinormal = cross(vertexNormal, vec3(vertexTangent));
|
|
||||||
|
|
||||||
// Calculate fragment normal based on normal transformations
|
|
||||||
mat3 normalMatrix = transpose(inverse(mat3(matModel)));
|
|
||||||
|
|
||||||
// Calculate fragment position based on model transformations
|
|
||||||
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
|
|
||||||
|
|
||||||
// Send vertex attributes to fragment shader
|
|
||||||
fragTexCoord = vertexTexCoord;
|
|
||||||
fragNormal = normalize(normalMatrix*vertexNormal);
|
|
||||||
fragTangent = normalize(normalMatrix*vec3(vertexTangent));
|
|
||||||
fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
|
|
||||||
fragBinormal = normalize(normalMatrix*vertexBinormal);
|
|
||||||
fragBinormal = cross(fragNormal, fragTangent);
|
|
||||||
|
|
||||||
// Calculate final vertex position
|
|
||||||
gl_Position = mvp*vec4(vertexPosition, 1.0);
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
#version 330
|
|
||||||
|
|
||||||
#define MAX_SAMPLES 1024u
|
|
||||||
#define CUBEMAP_RESOLUTION 1024.0
|
|
||||||
|
|
||||||
// Input vertex attributes (from vertex shader)
|
|
||||||
in vec3 fragPosition;
|
|
||||||
|
|
||||||
// Input uniform values
|
|
||||||
uniform samplerCube environmentMap;
|
|
||||||
uniform float roughness;
|
|
||||||
|
|
||||||
// Constant values
|
|
||||||
const float PI = 3.14159265359;
|
|
||||||
|
|
||||||
// Output fragment color
|
|
||||||
out vec4 finalColor;
|
|
||||||
|
|
||||||
float DistributionGGX(vec3 N, vec3 H, float roughness);
|
|
||||||
float RadicalInverse_VdC(uint bits);
|
|
||||||
vec2 Hammersley(uint i, uint N);
|
|
||||||
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness);
|
|
||||||
|
|
||||||
float DistributionGGX(vec3 N, vec3 H, float roughness)
|
|
||||||
{
|
|
||||||
float a = roughness*roughness;
|
|
||||||
float a2 = a*a;
|
|
||||||
float NdotH = max(dot(N, H), 0.0);
|
|
||||||
float NdotH2 = NdotH*NdotH;
|
|
||||||
|
|
||||||
float nom = a2;
|
|
||||||
float denom = (NdotH2*(a2 - 1.0) + 1.0);
|
|
||||||
denom = PI*denom*denom;
|
|
||||||
|
|
||||||
return nom/denom;
|
|
||||||
}
|
|
||||||
|
|
||||||
float RadicalInverse_VdC(uint bits)
|
|
||||||
{
|
|
||||||
bits = (bits << 16u) | (bits >> 16u);
|
|
||||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
|
||||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
|
||||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
|
||||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
|
||||||
return float(bits)*2.3283064365386963e-10; // / 0x100000000
|
|
||||||
}
|
|
||||||
|
|
||||||
vec2 Hammersley(uint i, uint N)
|
|
||||||
{
|
|
||||||
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)
|
|
||||||
{
|
|
||||||
float a = roughness*roughness;
|
|
||||||
float phi = 2.0*PI*Xi.x;
|
|
||||||
float cosTheta = sqrt((1.0 - Xi.y)/(1.0 + (a*a - 1.0)*Xi.y));
|
|
||||||
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
|
|
||||||
|
|
||||||
// Transform from spherical coordinates to cartesian coordinates (halfway vector)
|
|
||||||
vec3 H = vec3(cos(phi)*sinTheta, sin(phi)*sinTheta, cosTheta);
|
|
||||||
|
|
||||||
// Transform from tangent space H vector to world space sample vector
|
|
||||||
vec3 up = ((abs(N.z) < 0.999) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0));
|
|
||||||
vec3 tangent = normalize(cross(up, N));
|
|
||||||
vec3 bitangent = cross(N, tangent);
|
|
||||||
vec3 sampleVec = tangent*H.x + bitangent*H.y + N*H.z;
|
|
||||||
|
|
||||||
return normalize(sampleVec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Make the simplyfying assumption that V equals R equals the normal
|
|
||||||
vec3 N = normalize(fragPosition);
|
|
||||||
vec3 R = N;
|
|
||||||
vec3 V = R;
|
|
||||||
|
|
||||||
vec3 prefilteredColor = vec3(0.0);
|
|
||||||
float totalWeight = 0.0;
|
|
||||||
|
|
||||||
for (uint i = 0u; i < MAX_SAMPLES; i++)
|
|
||||||
{
|
|
||||||
// Generate a sample vector that's biased towards the preferred alignment direction (importance sampling)
|
|
||||||
vec2 Xi = Hammersley(i, MAX_SAMPLES);
|
|
||||||
vec3 H = ImportanceSampleGGX(Xi, N, roughness);
|
|
||||||
vec3 L = normalize(2.0*dot(V, H)*H - V);
|
|
||||||
|
|
||||||
float NdotL = max(dot(N, L), 0.0);
|
|
||||||
if(NdotL > 0.0)
|
|
||||||
{
|
|
||||||
// Sample from the environment's mip level based on roughness/pdf
|
|
||||||
float D = DistributionGGX(N, H, roughness);
|
|
||||||
float NdotH = max(dot(N, H), 0.0);
|
|
||||||
float HdotV = max(dot(H, V), 0.0);
|
|
||||||
float pdf = D*NdotH/(4.0*HdotV) + 0.0001;
|
|
||||||
|
|
||||||
float resolution = CUBEMAP_RESOLUTION;
|
|
||||||
float saTexel = 4.0*PI/(6.0*resolution*resolution);
|
|
||||||
float saSample = 1.0/(float(MAX_SAMPLES)*pdf + 0.0001);
|
|
||||||
float mipLevel = ((roughness == 0.0) ? 0.0 : 0.5*log2(saSample/saTexel));
|
|
||||||
|
|
||||||
prefilteredColor += textureLod(environmentMap, L, mipLevel).rgb*NdotL;
|
|
||||||
totalWeight += NdotL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate prefilter average color
|
|
||||||
prefilteredColor = prefilteredColor/totalWeight;
|
|
||||||
|
|
||||||
// Calculate final fragment color
|
|
||||||
finalColor = vec4(prefilteredColor, 1.0);
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue