diff --git a/src/models.c b/src/models.c index 94cae99f0..69bb322ca 100644 --- a/src/models.c +++ b/src/models.c @@ -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; } diff --git a/src/rlgl.c b/src/rlgl.c index 65615c7a1..2746c2d26 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -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) { diff --git a/src/rlgl.h b/src/rlgl.h index 38de423d2..895de17e1 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -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