Working on PBR support

Environment maps generation moved to rlgl
This commit is contained in:
Ray 2017-07-09 02:48:54 +02:00
parent 007cbf85b0
commit 847e7d56f0
3 changed files with 322 additions and 264 deletions

View File

@ -712,7 +712,7 @@ Material LoadMaterialDefault(void)
}
// Load PBR material (Supports: ALBEDO, NORMAL, METALNESS, ROUGHNESS, AO, EMMISIVE, HEIGHT maps)
Material LoadMaterialPBR(Texture2D cubemap, Color albedo, int metalness, int roughness)
Material LoadMaterialPBR(Texture2D hdr, Color albedo, int metalness, int roughness)
{
Material mat = { 0 };
@ -740,19 +740,27 @@ Material LoadMaterialPBR(Texture2D cubemap, Color albedo, int metalness, int rou
// Set up material properties color
mat.maps[TEXMAP_ALBEDO].color = albedo;
mat.maps[TEXMAP_NORMAL].color = (Color){ 128, 128, 255, 255 };
mat.maps[TEXMAP_METALNESS].value = metalness; //(Color){ metalness, 0, 0, 0 };
mat.maps[TEXMAP_ROUGHNESS].value = roughness; //(Color){ roughness, 0, 0, 0 };
mat.maps[TEXMAP_OCCLUSION].value = 1.0f; //(Color){ 255, 255, 255, 255 };
mat.maps[TEXMAP_EMISSION].value = 0.0f; //(Color){ 0, 0, 0, 0 };
mat.maps[TEXMAP_HEIGHT].value = 0.0f; //(Color){ 0, 0, 0, 0 };
mat.maps[TEXMAP_METALNESS].value = metalness;
mat.maps[TEXMAP_ROUGHNESS].value = roughness;
mat.maps[TEXMAP_OCCLUSION].value = 1.0f;
mat.maps[TEXMAP_EMISSION].value = 0.0f;
mat.maps[TEXMAP_HEIGHT].value = 0.0f;
#define CUBEMAP_SIZE 1024 // Cubemap texture size
#define IRRADIANCE_SIZE 32 // Irradiance map from cubemap texture size
#define PREFILTERED_SIZE 256 // Prefiltered HDR environment map texture size
#define BRDF_SIZE 512 // BRDF LUT texture map size
// Set up environment materials cubemap
mat.maps[TEXMAP_CUBEMAP].tex = cubemap; // Texture2D rlGenMapCubemap(Shader shader, Texture2D cubemap, int size);
//mat.maps[TEXMAP_IRRADIANCE] = env.maps[TEXMAP_IRRADIANCE]; // Texture2D GenMapIrradiance(Texture2D cubemap, int size);
//mat.maps[TEXMAP_PREFILTER] = env.maps[TEXMAP_PREFILTER]; // Texture2D GenMapPrefilter(Texture2D cubemap, int size);
//mat.maps[TEXMAP_BRDF] = env.maps[TEXMAP_BRDF]; // Texture2D GenMapBRDF(Texture2D cubemap, int size);
mat.maps[TEXMAP_CUBEMAP].tex = rlGenMapCubemap(hdr, CUBEMAP_SIZE);
mat.maps[TEXMAP_IRRADIANCE].tex = rlGenMapIrradiance(mat.maps[TEXMAP_CUBEMAP].tex, IRRADIANCE_SIZE);
mat.maps[TEXMAP_PREFILTER].tex = rlGenMapPrefilter(mat.maps[TEXMAP_CUBEMAP].tex, PREFILTERED_SIZE);
mat.maps[TEXMAP_BRDF].tex = rlGenMapBRDF(mat.maps[TEXMAP_CUBEMAP].tex, BRDF_SIZE);
// NOTE: All maps textures are set to { 0 }
// Reset viewport dimensions to default
rlViewport(0, 0, GetScreenWidth(), GetScreenHeight());
return mat;
}
@ -766,13 +774,6 @@ Material LoadMaterialEnv(const char *filename, int cubemapSize, int irradianceSi
#define PATH_SKYBOX_VS "resources/shaders/skybox.vs" // Path to skybox vertex shader
#define PATH_SKYBOX_FS "resources/shaders/skybox.fs" // Path to skybox vertex shader
#define PATH_CUBEMAP_VS "resources/shaders/cubemap.vs" // Path to equirectangular to cubemap vertex shader
#define PATH_CUBEMAP_FS "resources/shaders/cubemap.fs" // Path to equirectangular to cubemap fragment shader
#define PATH_IRRADIANCE_FS "resources/shaders/irradiance.fs" // Path to irradiance (GI) calculation fragment shader
#define PATH_PREFILTER_FS "resources/shaders/prefilter.fs" // Path to reflection prefilter calculation fragment shader
#define PATH_BRDF_VS "resources/shaders/brdf.vs" // Path to bidirectional reflectance distribution function vertex shader
#define PATH_BRDF_FS "resources/shaders/brdf.fs" // Path to bidirectional reflectance distribution function fragment shader
#define LOC_CUSTOM_SKYRESOLUTION 15
env.shader = LoadShader(PATH_SKYBOX_VS, PATH_SKYBOX_FS);
@ -785,258 +786,11 @@ Material LoadMaterialEnv(const char *filename, int cubemapSize, int irradianceSi
// Set up shaders constant values
SetShaderValuei(env.shader, GetShaderLocation(env.shader, "cubeMap"), (int[1]){ 0 }, 1);
// Load HDR environment texture
Texture2D skyTex = LoadTexture(filename);
// Generate texture: CUBEMAP
//----------------------------------------
Shader cubemapShader = LoadShader(PATH_CUBEMAP_VS, PATH_CUBEMAP_FS);
// Get cubemap shader locations
int cubeProjectionLoc = GetShaderLocation(cubemapShader, "projection");
int cubeViewLoc = GetShaderLocation(cubemapShader, "view");
SetShaderValuei(cubemapShader, GetShaderLocation(cubemapShader, "equirectangularMap"), (int[1]){ 0 }, 1);
/*
// Set up depth face culling and cube map seamless
glDepthFunc(GL_LEQUAL);
glDisable(GL_CULL_FACE);
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
glLineWidth(2);
// Set up framebuffer for skybox
unsigned int captureFBO, captureRBO;
glGenFramebuffers(1, &captureFBO);
glGenRenderbuffers(1, &captureRBO);
glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, cubemapSize, cubemapSize);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO);
// Set up cubemap to render and attach to framebuffer
// NOTE: faces are stored with 16 bit floating point values
glGenTextures(1, &env.maps[TEXMAP_CUBEMAP].tex.id);
glBindTexture(GL_TEXTURE_CUBE_MAP, env.maps[TEXMAP_CUBEMAP].tex.id);
for (unsigned int i = 0; i < 6; i++) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, cubemapSize, cubemapSize, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Create projection (transposed) and different views for each face
Matrix captureProjection = MatrixPerspective(90.0f, 1.0f, 0.01, 1000.0);
MatrixTranspose(&captureProjection);
Matrix captureViews[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 })
};
// Convert HDR equirectangular environment map to cubemap equivalent
glUseProgram(cubemapShader.id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, skyTex.id);
SetShaderValueMatrix(cubemapShader, cubeProjectionLoc, captureProjection);
// Note: don't forget to configure the viewport to the capture dimensions
rlViewport(0, 0, cubemapSize, cubemapSize);
rlEnableRenderTexture(captureFBO);
for (unsigned int i = 0; i < 6; i++)
{
SetShaderValueMatrix(cubemapShader, cubeViewLoc, captureViews[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, env.maps[TEXMAP_CUBEMAP].tex.id, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
RenderCube();
}
// Unbind framebuffer and textures
rlEnableRenderTexture(0);
*/
UnloadShader(cubemapShader);
//----------------------------------------
// Generate texture: IRRADIANCE
//----------------------------------------
Shader irradianceShader = LoadShader(PATH_SKYBOX_VS, PATH_IRRADIANCE_FS);
// Get irradiance shader locations
int irradianceProjectionLoc = GetShaderLocation(irradianceShader, "projection");
int irradianceViewLoc = GetShaderLocation(irradianceShader, "view");
// Set up shaders constant values
SetShaderValuei(irradianceShader, GetShaderLocation(irradianceShader, "environmentMap"), (int[1]){ 0 }, 1);
/*
// Create an irradiance cubemap, and re-scale capture FBO to irradiance scale
glGenTextures(1, &env.maps[TEXMAP_IRRADIANCE].tex.id);
glBindTexture(GL_TEXTURE_CUBE_MAP, env.maps[TEXMAP_IRRADIANCE].tex.id);
for (unsigned int i = 0; i < 6; i++) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, irradianceSize, irradianceSize, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, irradianceSize, irradianceSize);
// Solve diffuse integral by convolution to create an irradiance cubemap
glUseProgram(irradianceShader.id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, env.maps[TEXMAP_CUBEMAP].tex.id);
SetShaderValueMatrix(irradianceShader, irradianceProjectionLoc, captureProjection);
// Note: don't forget to configure the viewport to the capture dimensions
rlViewport(0, 0, irradianceSize, irradianceSize);
glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
for (unsigned int i = 0; i < 6; i++)
{
SetShaderValueMatrix(irradianceShader, irradianceViewLoc, captureViews[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, env.maps[TEXMAP_IRRADIANCE].tex.id, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
RenderCube();
}
// Unbind framebuffer and textures
glBindFramebuffer(GL_FRAMEBUFFER, 0);
*/
UnloadShader(irradianceShader);
//----------------------------------------
// Generate texture: PREFILTER
//----------------------------------------
Shader prefilterShader = LoadShader(PATH_SKYBOX_VS, PATH_PREFILTER_FS);
// Get prefilter shader locations
int prefilterProjectionLoc = GetShaderLocation(prefilterShader, "projection");
int prefilterViewLoc = GetShaderLocation(prefilterShader, "view");
int prefilterRoughnessLoc = GetShaderLocation(prefilterShader, "roughness");
SetShaderValuei(prefilterShader, GetShaderLocation(prefilterShader, "environmentMap"), (int[1]){ 0 }, 1);
/*
// Create a prefiltered HDR environment map
glGenTextures(1, &env.maps[TEXMAP_PREFILTER].tex.id);
glBindTexture(GL_TEXTURE_CUBE_MAP, env.maps[TEXMAP_PREFILTER].tex.id);
for (unsigned int i = 0; i < 6; i++) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, prefilterSize, prefilterSize, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Generate mipmaps for the prefiltered HDR texture
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
// Prefilter HDR and store data into mipmap levels
glUseProgram(prefilterShader.id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, env.maps[TEXMAP_CUBEMAP].tex.id);
SetShaderValueMatrix(prefilterShader, prefilterProjectionLoc, captureProjection);
glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
for (unsigned int mip = 0; mip < MAX_MIPMAP_LEVELS; mip++)
{
// Resize framebuffer according to mip-level size.
unsigned int mipWidth = prefilterSize*powf(0.5f, mip);
unsigned int mipHeight = prefilterSize*powf(0.5f, mip);
glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight);
glViewport(0, 0, mipWidth, mipHeight);
float roughness = (float)mip/(float)(MAX_MIPMAP_LEVELS - 1);
glUniform1f(prefilterRoughnessLoc, roughness);
for (unsigned int i = 0; i < 6; ++i)
{
SetShaderValueMatrix(prefilterShader, prefilterViewLoc, captureViews[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, env.maps[TEXMAP_PREFILTER].tex.id, mip);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
RenderCube();
}
}
// Unbind framebuffer and textures
glBindFramebuffer(GL_FRAMEBUFFER, 0);
*/
UnloadShader(prefilterShader);
//----------------------------------------
// Generate texture: BRDF
//----------------------------------------
Shader brdfShader = LoadShader(PATH_BRDF_VS, PATH_BRDF_FS);
/*
RenderTexture2D brdfMap = LoadRenderTexture(brdfSize, brdfSize);
BeginDrawing();
BeginTextureMode(brdfMap);
rlViewport(0, 0, brdfSize, brdfSize);
BeginShaderMode(brdfShader);
RenderQuad();
EndShaderMode();
EndTextureMode();
EndDrawing();
UnloadRenderTexture(brdfMap);
*/
/*
// Generate BRDF convolution texture
glGenTextures(1, &env.maps[TEXMAP_BRDF].tex.id);
glBindTexture(GL_TEXTURE_2D, env.maps[TEXMAP_BRDF].tex.id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, brdfSize, brdfSize, 0, GL_RG, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Render BRDF LUT into a quad using default FBO
glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, brdfSize, brdfSize);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, env.maps[TEXMAP_BRDF].tex.id, 0);
rlViewport(0, 0, brdfSize, brdfSize);
glUseProgram(brdfShader.id);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
RenderQuad();
// Unbind framebuffer and textures
glBindFramebuffer(GL_FRAMEBUFFER, 0);
*/
// Then before rendering, configure the viewport to the actual screen dimensions
Matrix defaultProjection = MatrixPerspective(60.0, (double)GetScreenWidth()/(double)GetScreenHeight(), 0.01, 1000.0);
MatrixTranspose(&defaultProjection);
SetShaderValueMatrix(env.shader, skyProjectionLoc, defaultProjection);
//SetShaderValueMatrix(cubemapShader, cubeProjectionLoc, defaultProjection); // Not required any more
//SetShaderValueMatrix(irradianceShader, irradianceProjectionLoc, defaultProjection); // Not required any more
//SetShaderValueMatrix(prefilterShader, prefilterProjectionLoc, defaultProjection); // Not required any more
UnloadShader(brdfShader);
//----------------------------------------
// Reset viewport dimensions to default
rlViewport(0, 0, GetScreenWidth(), GetScreenHeight());
return env;
}

View File

@ -1714,6 +1714,305 @@ void rlGenerateMipmaps(Texture2D *texture)
glBindTexture(GL_TEXTURE_2D, 0);
}
// Generated cubemap texture
Texture2D rlGenMapCubemap(Texture2D skyHDR, int size)
{
Texture2D cubemap = { 0 };
#define PATH_CUBEMAP_VS "resources/shaders/cubemap.vs" // Path to equirectangular to cubemap vertex shader
#define PATH_CUBEMAP_FS "resources/shaders/cubemap.fs" // Path to equirectangular to cubemap fragment shader
Shader shader = LoadShader(PATH_CUBEMAP_VS, PATH_CUBEMAP_FS);
// Get cubemap shader locations
int projectionLoc = GetShaderLocation(shader, "projection");
int viewLoc = GetShaderLocation(shader, "view");
SetShaderValuei(shader, GetShaderLocation(shader, "equirectangularMap"), (int[1]){ 0 }, 1);
// Set up depth face culling and cubemap seamless
// TODO: Review all those functions
glDepthFunc(GL_LEQUAL);
glDisable(GL_CULL_FACE);
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
glLineWidth(2);
// Setup framebuffer
unsigned int fbo, rbo;
glGenFramebuffers(1, &fbo);
glGenRenderbuffers(1, &rbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
// Set up cubemap to render and attach to framebuffer
// NOTE: faces are stored with 16 bit floating point values
glGenTextures(1, &cubemap.id);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id);
for (unsigned int i = 0; i < 6; i++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Create projection (transposed) and different views for each face
Matrix captureProjection = MatrixPerspective(90.0f, 1.0f, 0.01, 1000.0);
MatrixTranspose(&captureProjection);
Matrix captureViews[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 })
};
// Convert HDR equirectangular environment map to cubemap equivalent
glUseProgram(shader.id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, skyHDR.id);
SetShaderValueMatrix(shader, projectionLoc, captureProjection);
// Note: don't forget to configure the viewport to the capture dimensions
rlViewport(0, 0, size, size);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
for (unsigned int i = 0; i < 6; i++)
{
SetShaderValueMatrix(shader, viewLoc, captureViews[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap.id, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// TODO: RenderCube();
}
// Unbind framebuffer and textures
glBindFramebuffer(GL_FRAMEBUFFER, 0);
UnloadShader(shader);
cubemap.width = size;
cubemap.height = size;
return cubemap;
}
Texture2D rlGenMapIrradiance(Texture2D cubemap, int size)
{
Texture2D irradiance = { 0 };
#define PATH_SKYBOX_VS "resources/shaders/skybox.vs" // Path to skybox vertex shader
#define PATH_IRRADIANCE_FS "resources/shaders/irradiance.fs" // Path to irradiance (GI) calculation fragment shader
Shader shader = LoadShader(PATH_SKYBOX_VS, PATH_IRRADIANCE_FS);
// Get irradiance shader locations
int projectionLoc = GetShaderLocation(shader, "projection");
int viewLoc = GetShaderLocation(shader, "view");
// Set up shaders constant values
SetShaderValuei(shader, GetShaderLocation(shader, "environmentMap"), (int[1]){ 0 }, 1);
// Setup framebuffer
unsigned int fbo, rbo;
glGenFramebuffers(1, &fbo);
glGenRenderbuffers(1, &rbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
// Create an irradiance cubemap, and re-scale capture FBO to irradiance scale
glGenTextures(1, &irradiance.id);
glBindTexture(GL_TEXTURE_CUBE_MAP, irradiance.id);
for (unsigned int i = 0; i < 6; i++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Create projection (transposed) and different views for each face
Matrix captureProjection = MatrixPerspective(90.0f, 1.0f, 0.01, 1000.0);
MatrixTranspose(&captureProjection);
Matrix captureViews[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 })
};
// Solve diffuse integral by convolution to create an irradiance cubemap
glUseProgram(shader.id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id);
SetShaderValueMatrix(shader, projectionLoc, captureProjection);
// Note: don't forget to configure the viewport to the capture dimensions
rlViewport(0, 0, size, size);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
for (unsigned int i = 0; i < 6; i++)
{
SetShaderValueMatrix(shader, viewLoc, captureViews[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradiance.id, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// TODO: RenderCube();
}
// Unbind framebuffer and textures
glBindFramebuffer(GL_FRAMEBUFFER, 0);
UnloadShader(shader);
irradiance.width = size;
irradiance.height = size;
return irradiance;
}
Texture2D rlGenMapPrefilter(Texture2D cubemap, int size)
{
Texture2D prefilter = { 0 };
#define PATH_SKYBOX_VS "resources/shaders/skybox.vs" // Path to skybox vertex shader
#define PATH_PREFILTER_FS "resources/shaders/prefilter.fs" // Path to reflection prefilter calculation fragment shader
Shader shader = LoadShader(PATH_SKYBOX_VS, PATH_PREFILTER_FS);
// Get prefilter shader locations
int projectionLoc = GetShaderLocation(shader, "projection");
int viewLoc = GetShaderLocation(shader, "view");
int roughnessLoc = GetShaderLocation(shader, "roughness");
SetShaderValuei(shader, GetShaderLocation(shader, "environmentMap"), (int[1]){ 0 }, 1);
// Setup framebuffer
unsigned int fbo, rbo;
glGenFramebuffers(1, &fbo);
glGenRenderbuffers(1, &rbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
// Create a prefiltered HDR environment map
glGenTextures(1, &prefilter.id);
glBindTexture(GL_TEXTURE_CUBE_MAP, prefilter.id);
for (unsigned int i = 0; i < 6; i++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Generate mipmaps for the prefiltered HDR texture
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
// Create projection (transposed) and different views for each face
Matrix captureProjection = MatrixPerspective(90.0f, 1.0f, 0.01, 1000.0);
MatrixTranspose(&captureProjection);
Matrix captureViews[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 })
};
// Prefilter HDR and store data into mipmap levels
glUseProgram(shader.id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id);
SetShaderValueMatrix(shader, projectionLoc, captureProjection);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
#define MAX_MIPMAP_LEVELS 5 // Max number of prefilter texture mipmaps
for (unsigned int mip = 0; mip < MAX_MIPMAP_LEVELS; mip++)
{
// Resize framebuffer according to mip-level size.
unsigned int mipWidth = size*powf(0.5f, mip);
unsigned int mipHeight = size*powf(0.5f, mip);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight);
glViewport(0, 0, mipWidth, mipHeight);
float roughness = (float)mip/(float)(MAX_MIPMAP_LEVELS - 1);
glUniform1f(roughnessLoc, roughness);
for (unsigned int i = 0; i < 6; ++i)
{
SetShaderValueMatrix(shader, viewLoc, captureViews[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilter.id, mip);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// TODO: RenderCube();
}
}
// Unbind framebuffer and textures
glBindFramebuffer(GL_FRAMEBUFFER, 0);
UnloadShader(shader);
prefilter.width = size;
prefilter.height = size;
return prefilter;
}
Texture2D rlGenMapBRDF(Texture2D cubemap, int size)
{
Texture2D brdf = { 0 };
#define PATH_BRDF_VS "resources/shaders/brdf.vs" // Path to bidirectional reflectance distribution function vertex shader
#define PATH_BRDF_FS "resources/shaders/brdf.fs" // Path to bidirectional reflectance distribution function fragment shader
Shader shader = LoadShader(PATH_BRDF_VS, PATH_BRDF_FS);
// Generate BRDF convolution texture
glGenTextures(1, &brdf.id);
glBindTexture(GL_TEXTURE_2D, brdf.id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, size, size, 0, GL_RG, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Render BRDF LUT into a quad using FBO
unsigned int fbo, rbo;
glGenFramebuffers(1, &fbo);
glGenRenderbuffers(1, &rbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, brdf.id, 0);
rlViewport(0, 0, size, size);
glUseProgram(shader.id);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// TODO: RenderQuad();
// Unbind framebuffer and textures
glBindFramebuffer(GL_FRAMEBUFFER, 0);
UnloadShader(shader);
brdf.width = size;
brdf.height = size;
return brdf;
}
// Upload vertex data into a VAO (if supported) and VBO
void rlLoadMesh(Mesh *mesh, bool dynamic)
{

View File

@ -417,6 +417,11 @@ void rlUnloadMesh(Mesh *mesh); // Unload mesh
Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates
Texture2D rlGenMapCubemap(Texture2D cubemap, int size);
Texture2D rlGenMapIrradiance(Texture2D cubemap, int size);
Texture2D rlGenMapPrefilter(Texture2D cubemap, int size);
Texture2D rlGenMapBRDF(Texture2D cubemap, int size);
// NOTE: There is a set of shader related functions that are available to end user,
// to avoid creating function wrappers through core module, they have been directly declared in raylib.h