From b1f444e547c7b85bfac11fd73063ed1b7e4eadd6 Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Mon, 1 Jun 2026 17:13:22 +0800 Subject: [PATCH 01/11] Refactor shader naming and update BasicLightingScene lighting parameters - Rename shader assets to remove numeric prefixes and update corresponding paths in `BasicLightingScene` and `MultiLightsScene`. - Reorganize vertex data in `BasicLightingScene` with clearer face labeling. - Adjust lighting and material properties in `BasicLightingScene`, including ambient/diffuse intensities and shininess calculation. - Update clear color and integrate OpenGL error checking using `check_gl_error()`. - Refine code formatting and pointer syntax for consistency. --- .../{5.4.light_casters.frag => light_casters.frag} | 4 ++-- .../{5.4.light_casters.vert => light_casters.vert} | 0 .../{5.4.light_cube.frag => light_cube.frag} | 0 .../{5.4.light_cube.vert => light_cube.vert} | 0 tutorial/src/main/cpp/TargetCamera.cpp | 2 ++ tutorial/src/main/cpp/lighting/LightCasterScene.cpp | 12 +++++++----- 6 files changed, 11 insertions(+), 7 deletions(-) rename tutorial/src/main/assets/shaders/light_caster/{5.4.light_casters.frag => light_casters.frag} (96%) rename tutorial/src/main/assets/shaders/light_caster/{5.4.light_casters.vert => light_casters.vert} (100%) rename tutorial/src/main/assets/shaders/light_caster/{5.4.light_cube.frag => light_cube.frag} (100%) rename tutorial/src/main/assets/shaders/light_caster/{5.4.light_cube.vert => light_cube.vert} (100%) diff --git a/tutorial/src/main/assets/shaders/light_caster/5.4.light_casters.frag b/tutorial/src/main/assets/shaders/light_caster/light_casters.frag similarity index 96% rename from tutorial/src/main/assets/shaders/light_caster/5.4.light_casters.frag rename to tutorial/src/main/assets/shaders/light_caster/light_casters.frag index 32fe2c34..294e0b16 100644 --- a/tutorial/src/main/assets/shaders/light_caster/5.4.light_casters.frag +++ b/tutorial/src/main/assets/shaders/light_caster/light_casters.frag @@ -58,9 +58,9 @@ void main() // attenuation float distance = length(light.position - FragPos); float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); - ambient *= attenuation; + ambient *= attenuation; diffuse *= attenuation; - specular *= attenuation; + specular *= attenuation; vec3 result = ambient + diffuse + specular; FragColor = vec4(result, 1.0); diff --git a/tutorial/src/main/assets/shaders/light_caster/5.4.light_casters.vert b/tutorial/src/main/assets/shaders/light_caster/light_casters.vert similarity index 100% rename from tutorial/src/main/assets/shaders/light_caster/5.4.light_casters.vert rename to tutorial/src/main/assets/shaders/light_caster/light_casters.vert diff --git a/tutorial/src/main/assets/shaders/light_caster/5.4.light_cube.frag b/tutorial/src/main/assets/shaders/light_caster/light_cube.frag similarity index 100% rename from tutorial/src/main/assets/shaders/light_caster/5.4.light_cube.frag rename to tutorial/src/main/assets/shaders/light_caster/light_cube.frag diff --git a/tutorial/src/main/assets/shaders/light_caster/5.4.light_cube.vert b/tutorial/src/main/assets/shaders/light_caster/light_cube.vert similarity index 100% rename from tutorial/src/main/assets/shaders/light_caster/5.4.light_cube.vert rename to tutorial/src/main/assets/shaders/light_caster/light_cube.vert diff --git a/tutorial/src/main/cpp/TargetCamera.cpp b/tutorial/src/main/cpp/TargetCamera.cpp index 186e1573..fcd7dc7a 100644 --- a/tutorial/src/main/cpp/TargetCamera.cpp +++ b/tutorial/src/main/cpp/TargetCamera.cpp @@ -100,6 +100,8 @@ void TargetCamera::update() { glm::to_string(m_up).c_str()); m_viewMatrix = glm::lookAt(m_position, m_targetPosition, m_up); + m_front = glm::normalize(m_targetPosition - m_position); + m_right = glm::normalize(glm::cross(m_front, m_up)); viewDirty = false; } if (projectionDirty) { diff --git a/tutorial/src/main/cpp/lighting/LightCasterScene.cpp b/tutorial/src/main/cpp/lighting/LightCasterScene.cpp index 115f1086..f49b680d 100644 --- a/tutorial/src/main/cpp/lighting/LightCasterScene.cpp +++ b/tutorial/src/main/cpp/lighting/LightCasterScene.cpp @@ -25,8 +25,10 @@ void LightCasterScene::init() { // build and compile our shader zprogram // ------------------------------------ - m_pLightingShader = new Shader("shaders/light_caster/5.4.light_casters.vert", "shaders/light_caster/5.4.light_casters.frag"); - m_pLightCubeShader = new Shader("shaders/light_caster/5.4.light_cube.vert", "shaders/light_caster/5.4.light_cube.frag"); + m_pLightingShader = new Shader("shaders/light_caster/light_casters.vert", + "shaders/light_caster/light_casters.frag"); + m_pLightCubeShader = new Shader("shaders/light_caster/light_cube.vert", + "shaders/light_caster/light_cube.frag"); // set up vertex data (and buffer(s)) and configure vertex attributes // ------------------------------------------------------------------ @@ -126,12 +128,12 @@ void LightCasterScene::draw() { m_pLightingShader->use(); m_pLightingShader->setVec3("light.position", m_camera->getPosition()); m_pLightingShader->setVec3("light.direction", m_camera->getFront()); - m_pLightingShader->setFloat("light.cutOff", glm::cos(glm::radians(12.5f))); - m_pLightingShader->setFloat("light.outerCutOff", glm::cos(glm::radians(17.5f))); + m_pLightingShader->setFloat("light.cutOff", glm::cos(glm::radians(6.5f))); + m_pLightingShader->setFloat("light.outerCutOff", glm::cos(glm::radians(9.5f))); m_pLightingShader->setVec3("viewPos", m_camera->getPosition()); // light properties - m_pLightingShader->setVec3("light.ambient", 0.1f, 0.1f, 0.1f); + m_pLightingShader->setVec3("light.ambient", 0.35f, 0.35f, 0.35f); m_pLightingShader->setVec3("light.diffuse", 0.8f, 0.8f, 0.8f); m_pLightingShader->setVec3("light.specular", 1.0f, 1.0f, 1.0f); m_pLightingShader->setFloat("light.constant", 1.0f); From 1b8776457c251a7bbac54c300064a3b0c1f6b4bb Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Mon, 1 Jun 2026 20:58:25 +0800 Subject: [PATCH 02/11] Update BasicLightingScene with OpenGL error handling and vertex data organization - Integrated `glerror.h` and added `check_gl_error()` calls in `init`, `draw`, and `destroy`. - Reformatted the cube vertex array with face-specific comments and improved alignment. - Updated the material shininess calculation and initialized the light position in the constructor. - Applied general code style and formatting improvements. --- .../main/cpp/lighting/BasicLightingScene.cpp | 83 ++++++++++--------- 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp b/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp index 79733d9e..8303b1db 100644 --- a/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp +++ b/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp @@ -4,8 +4,10 @@ #include "Camera.h" #include "TargetCamera.h" #include "Texture.h" +#include "glerror.h" BasicLightingScene::BasicLightingScene() { + m_lightPos = glm::vec3(1.2f, 1.0f, 2.0f); } void BasicLightingScene::init() { @@ -26,20 +28,23 @@ void BasicLightingScene::init() { // ------------------------------------------------------------------ float vertices[] = { // positions // normals // texture coords - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, - -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, - + // front face -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + // back face + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + + // left face -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, @@ -47,24 +52,27 @@ void BasicLightingScene::init() { -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + // right face + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + // bottom face -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + // top face -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f }; @@ -76,11 +84,11 @@ void BasicLightingScene::init() { glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindVertexArray(m_cubeVAO); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) 0); glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) (3 * sizeof(float))); glEnableVertexAttribArray(1); - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) (6 * sizeof(float))); glEnableVertexAttribArray(2); // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube) @@ -89,7 +97,7 @@ void BasicLightingScene::init() { glBindBuffer(GL_ARRAY_BUFFER, m_VBO); // note that we update the lamp's position attribute's stride to reflect the updated buffer data - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) 0); glEnableVertexAttribArray(0); // load textures @@ -102,6 +110,8 @@ void BasicLightingScene::init() { m_pLightingShader->setInt("material.diffuse", 0); m_pLightingShader->setInt("material.specular", 1); m_pLightingShader->setInt("material.emission", 2); + + check_gl_error(); } void BasicLightingScene::resize(int width, int height) { @@ -129,7 +139,7 @@ void BasicLightingScene::draw() { m_pLightingShader->setVec3("light.specular", 1.0f, 1.0f, 1.0f); // material properties - m_pLightingShader->setFloat("material.shininess", 64.0f); + m_pLightingShader->setFloat("material.shininess", 64.f * 64.f); // view/projection transformations glm::mat4 projection = m_camera->getProjectionMatrix(); @@ -156,8 +166,7 @@ void BasicLightingScene::draw() { glDrawArrays(GL_TRIANGLES, 0, 36); // also draw the lamp object - if(m_pLightCubeShader) - { + if (m_pLightCubeShader) { m_pLightCubeShader->use(); m_pLightCubeShader->setMat4("projection", projection); m_pLightCubeShader->setMat4("view", view); @@ -170,23 +179,23 @@ void BasicLightingScene::draw() { glDrawArrays(GL_TRIANGLES, 0, 36); } } + check_gl_error(); } void BasicLightingScene::destroy() { glDeleteVertexArrays(1, &m_cubeVAO); glDeleteVertexArrays(1, &m_lightCubeVAO); glDeleteBuffers(1, &m_VBO); - if (m_pLightingShader) - { + if (m_pLightingShader) { delete m_pLightingShader; m_pLightingShader = nullptr; } - if (m_pLightCubeShader) - { + if (m_pLightCubeShader) { delete m_pLightCubeShader; m_pLightCubeShader = nullptr; } delete m_camera; + check_gl_error(); } BasicLightingScene::~BasicLightingScene() { @@ -205,12 +214,12 @@ std::map BasicLightingScene::propertyEvent(std::map &event) { - auto* targetCamera = dynamic_cast(m_camera); + auto *targetCamera = dynamic_cast(m_camera); if (!targetCamera) return; if (auto it = event.find("single_touching"); it != event.end()) { if (it->second.type() == typeid(std::vector)) { - const auto& val = std::any_cast&>(it->second); + const auto &val = std::any_cast &>(it->second); if (val.size() >= 4) { targetCamera->onSingleTouching(glm::vec2(val[0], val[1]), glm::vec2(val[2], val[3])); } @@ -219,10 +228,10 @@ void BasicLightingScene::parseTargetCameraEvent(std::map if (auto it = event.find("double_touching"); it != event.end()) { if (it->second.type() == typeid(std::vector)) { - const auto& val = std::any_cast&>(it->second); + const auto &val = std::any_cast &>(it->second); if (val.size() >= 8) { targetCamera->onDoubleTouching(glm::vec2(val[0], val[1]), glm::vec2(val[2], val[3]), - glm::vec2(val[4], val[5]), glm::vec2(val[6], val[7])); + glm::vec2(val[4], val[5]), glm::vec2(val[6], val[7])); } } } From 90ad235637aa2c650a5feb7bf0df3b3d1cc76ba2 Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Mon, 1 Jun 2026 22:14:49 +0800 Subject: [PATCH 03/11] Fixed the control panel event. --- .../com/minininja/learngles/GLActivity.kt | 62 +++++++++++--- .../lighting/BasicLightingActivity.kt | 82 ++++++++++++++++++- 2 files changed, 129 insertions(+), 15 deletions(-) diff --git a/tutorial/src/main/java/com/minininja/learngles/GLActivity.kt b/tutorial/src/main/java/com/minininja/learngles/GLActivity.kt index 6bc2239c..e1d1dac1 100644 --- a/tutorial/src/main/java/com/minininja/learngles/GLActivity.kt +++ b/tutorial/src/main/java/com/minininja/learngles/GLActivity.kt @@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.displayCutoutPadding import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -37,9 +38,15 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.boundsInRoot +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.minininja.learngles.ui.theme.LearnGLESTheme @@ -117,7 +124,8 @@ fun Modifier.layer3DTouch(callback: Layer3DTouchCallback?): Modifier = this while (true) { val event = awaitPointerEvent() val changes = event.changes - val pressedChanges = changes.filter { it.pressed } + // Only process changes that haven't been consumed by layers above (like ControlPanel) + val pressedChanges = changes.filter { it.pressed && !it.isConsumed } val currentPointerCount = pressedChanges.size // --- MULTI-FINGER TAP DETECTION --- @@ -179,14 +187,19 @@ fun OpenGLContainer( overlay: @Composable () -> Unit = {} ) { var isExpanded by remember { mutableStateOf(true) } + var panelBounds by remember { mutableStateOf(Rect.Zero) } Box( - modifier = Modifier - .fillMaxSize() - .layer3DTouch(touchCallback) // Apply the callback here + modifier = Modifier.fillMaxSize() ) { - AndroidView( - factory = { context -> + // Camera Layer (Bottom) + Box( + modifier = Modifier + .fillMaxSize() + .layer3DTouch(touchCallback) + ) { + AndroidView( + factory = { context -> GLSurfaceView(context).apply { setEGLContextClientVersion(3) setEGLConfigChooser(8, 8, 8, 8, 16, 8) @@ -227,14 +240,39 @@ fun OpenGLContainer( }, modifier = Modifier.fillMaxSize() ) + } + + // Shield Layer (Middle) - Catch events on ControlPanel's background + if (panelBounds != Rect.Zero) { + val density = LocalDensity.current Box( modifier = Modifier - .displayCutoutPadding() - .padding(top = 8.dp, start = 8.dp) - .background(Color.Black.copy(alpha = 0.3f), RoundedCornerShape(8.dp)) - .padding(if (isExpanded) 4.dp else 8.dp) - .wrapContentSize() - ) { + .offset { IntOffset(panelBounds.left.toInt(), panelBounds.top.toInt()) } + .size( + width = with(density) { panelBounds.width.toDp() }, + height = with(density) { panelBounds.height.toDp() } + ) + .pointerInput(Unit) { + awaitPointerEventScope { + while (true) { + awaitPointerEvent().changes.forEach { it.consume() } + } + } + } + ) + } + + Box( + modifier = Modifier + .onGloballyPositioned { coordinates -> + panelBounds = coordinates.boundsInRoot() + } + .displayCutoutPadding() + .padding(top = 8.dp, start = 8.dp) + .background(Color.Black.copy(alpha = 0.3f), RoundedCornerShape(8.dp)) + .padding(if (isExpanded) 4.dp else 8.dp) + .wrapContentSize() + ) { Column(modifier = Modifier.width(IntrinsicSize.Max)) { if (!isExpanded) { Icon( diff --git a/tutorial/src/main/java/com/minininja/learngles/lighting/BasicLightingActivity.kt b/tutorial/src/main/java/com/minininja/learngles/lighting/BasicLightingActivity.kt index bf97b26f..7dc97f55 100644 --- a/tutorial/src/main/java/com/minininja/learngles/lighting/BasicLightingActivity.kt +++ b/tutorial/src/main/java/com/minininja/learngles/lighting/BasicLightingActivity.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material3.Checkbox +import androidx.compose.material3.Slider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -16,13 +17,19 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import androidx.compose.ui.tooling.preview.Preview import com.minininja.learngles.GLActivity import com.minininja.learngles.Layer3DTouchCallback import com.minininja.learngles.NativeHelper +import com.minininja.learngles.ui.theme.LearnGLESTheme class BasicLightingActivity : GLActivity() { private var active by mutableStateOf(true) + private var ambientFactor by mutableStateOf(0.35f) + private var diffuseFactor by mutableStateOf(0.5f) + private var specularFactor by mutableStateOf(1.0f) + override fun createTouchCallback(): Layer3DTouchCallback { return object : Layer3DTouchCallback { @@ -85,26 +92,95 @@ class BasicLightingActivity : GLActivity() { onActiveChange = { active = it }, + ambientFactor = ambientFactor, + onAmbientFactorChange = { + Log.i(TAG, "onAmbientFactorChange [curr $it ] <=> [prev $ambientFactor]") + ambientFactor = it + updateProperty("ambientFactor", it) + }, + diffuseFactor = diffuseFactor, + onDiffuseFactorChange = { + diffuseFactor = it + updateProperty("diffuseFactor", it) + }, + specularFactor = specularFactor, + onSpecularFactorChange = { + specularFactor = it + updateProperty("specularFactor", it) + } ) } + + private fun updateProperty(name: String, value: Float) { + val event = mapOf(name to value) + NativeHelper.sendCommands(event) + glSurfaceView?.requestRender() + } + + companion object { + private const val TAG = "BasicLightingActivity" + } } @Composable private fun ControlPanelContent( active: Boolean, onActiveChange: (Boolean) -> Unit, + ambientFactor: Float, + onAmbientFactorChange: (Float) -> Unit, + diffuseFactor: Float, + onDiffuseFactorChange: (Float) -> Unit, + specularFactor: Float, + onSpecularFactorChange: (Float) -> Unit, modifier: Modifier = Modifier, ) { Column( - modifier = modifier.wrapContentSize() + modifier = modifier.wrapContentSize().padding(8.dp) ) { - Row(verticalAlignment = Alignment.CenterVertically, - modifier = modifier.padding(8.dp)) { + Row(verticalAlignment = Alignment.CenterVertically) { Text(text = "Target Camera Events", color = Color.White) Checkbox( checked = active, onCheckedChange = onActiveChange ) } + + FactorSlider(label = "Ambient Factor", value = ambientFactor, onValueChange = onAmbientFactorChange) + FactorSlider(label = "Diffuse Factor", value = diffuseFactor, onValueChange = onDiffuseFactorChange) + FactorSlider(label = "Specular Factor", value = specularFactor, onValueChange = onSpecularFactorChange) + } +} + +@Composable +private fun FactorSlider( + label: String, + value: Float, + onValueChange: (Float) -> Unit, + modifier: Modifier = Modifier +) { + Column(modifier = modifier.padding(vertical = 4.dp)) { + Text(text = "$label: ${"%.2f".format(value)}", color = Color.White) + Slider( + value = value, + onValueChange = onValueChange, + valueRange = 0f..1f + ) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFF000000) +@Composable +private fun ControlPanelPreview() { + LearnGLESTheme { + ControlPanelContent( + active = true, + onActiveChange = {}, + ambientFactor = 0.35f, + onAmbientFactorChange = {}, + diffuseFactor = 0.5f, + onDiffuseFactorChange = {}, + specularFactor = 1.0f, + onSpecularFactorChange = {} + ) } } From e87e2ae708a157b9f95c601480ff7837cbb9a186 Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Mon, 1 Jun 2026 22:19:46 +0800 Subject: [PATCH 04/11] Add SimplexTriangleEditor for interactive weight selection Implement a Jetpack Compose component that allows users to select normalized barycentric weights using an equilateral triangle interface. The component includes support for drag and tap gestures, along with logic for mapping screen coordinates to simplex weights. --- docs/SimplexTriangleEditor.md | 57 +++++++ .../learngles/ui/SimplexTriangleEditor.kt | 157 ++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 docs/SimplexTriangleEditor.md create mode 100644 tutorial/src/main/java/com/minininja/learngles/ui/SimplexTriangleEditor.kt diff --git a/docs/SimplexTriangleEditor.md b/docs/SimplexTriangleEditor.md new file mode 100644 index 00000000..5ad6488e --- /dev/null +++ b/docs/SimplexTriangleEditor.md @@ -0,0 +1,57 @@ +# SimplexTriangleEditor + +The `SimplexTriangleEditor` is a Jetpack Compose UI component that provides an interactive equilateral triangle for selecting barycentric weights. It is particularly useful for blending three different properties, such as colors, textures, or shader parameters. + +## Features + +- **Visual Interaction**: Drag or tap anywhere inside the triangle to move the selector handle. +- **Barycentric Mapping**: Automatically calculates three normalized weights ($a + b + c = 1.0$) based on the handle's position. +- **Responsive Layout**: Dynamically adjusts its geometry based on the available canvas size. +- **Visual Cues**: Colors the vertices (Red, Green, Blue) to represent the weight influence of each corner. + +## Usage + +### Data Model + +The widget uses a simple data class to hold the weights: + +```kotlin +data class SimplexWeights(val a: Float, val b: Float, val c: Float) +``` + +### Basic Example + +```kotlin +@Composable +fun WeightSelectionScreen() { + var weights by remember { + mutableStateOf(SimplexWeights(0.333f, 0.333f, 0.334f)) + } + + SimplexTriangleEditor( + modifier = Modifier.size(300.dp), + initialWeights = weights, + onWeightsChanged = { newWeights -> + weights = newWeights + // Update your logic or native shaders here + println("Weights: A=${newWeights.a}, B=${newWeights.b}, C=${newWeights.c}") + } + ) +} +``` + +## Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `modifier` | `Modifier` | Standard Compose modifier for layout and sizing. | +| `initialWeights` | `SimplexWeights` | The starting position of the selector handle. Defaults to center. | +| `onWeightsChanged` | `(SimplexWeights) -> Unit` | Callback triggered whenever the handle is moved or tapped. | + +## Implementation Details + +The widget implements **Inverse Kinematics** to map 2D pixel offsets from the screen into normalized barycentric coordinates. It ensures that even if a user taps slightly outside the triangle bounds, the resulting weights are clamped and re-normalized to maintain a valid state for rendering engines. + +--- + +*Part of the LearnGLES tutorial series.* diff --git a/tutorial/src/main/java/com/minininja/learngles/ui/SimplexTriangleEditor.kt b/tutorial/src/main/java/com/minininja/learngles/ui/SimplexTriangleEditor.kt new file mode 100644 index 00000000..43b72a2f --- /dev/null +++ b/tutorial/src/main/java/com/minininja/learngles/ui/SimplexTriangleEditor.kt @@ -0,0 +1,157 @@ +package com.minininja.learngles.ui + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.tooling.preview.Preview +import com.minininja.learngles.ui.theme.LearnGLESTheme +import kotlin.math.max +import kotlin.math.min +import kotlin.math.sqrt + +// Data class to hold our normalized weights +data class SimplexWeights(val a: Float, val b: Float, val c: Float) + +@Composable +fun SimplexTriangleEditor( + modifier: Modifier = Modifier, + initialWeights: SimplexWeights = SimplexWeights(0.333f, 0.333f, 0.334f), + onWeightsChanged: (SimplexWeights) -> Unit +) { + var weights by remember { mutableStateOf(initialWeights) } + + Box(modifier = modifier) { + Canvas( + modifier = Modifier + .fillMaxSize() + .pointerInput(Unit) { + // Handle dragging the handle + detectDragGestures { change, _ -> + change.consume() + val newWeights = calculateWeightsFromScreenPos( + pointerPos = change.position, + canvasWidth = size.width.toFloat(), + canvasHeight = size.height.toFloat() + ) + weights = newWeights + onWeightsChanged(newWeights) + } + } + .pointerInput(Unit) { + // Handle snapping to a direct tap location + detectTapGestures { offset -> + val newWeights = calculateWeightsFromScreenPos( + pointerPos = offset, + canvasWidth = size.width.toFloat(), + canvasHeight = size.height.toFloat() + ) + weights = newWeights + onWeightsChanged(newWeights) + } + } + ) { + // 1. Calculate Geometry Layout dynamically based on Canvas boundaries + val centerX = size.width / 2f + val centerY = size.height / 2f + // Use 85% of the shortest dimension as the bounding radius to ensure padding + val radius = min(size.width, size.height) * 0.425f + + val sqrt3Over2 = sqrt(3f) / 2f + + val vertA = Offset(centerX, centerY - radius) + val vertB = Offset(centerX - radius * sqrt3Over2, centerY + radius * 0.5f) + val vertC = Offset(centerX + radius * sqrt3Over2, centerY + radius * 0.5f) + + // 2. Draw the Equilateral Triangle Hull + val trianglePath = Path().apply { + moveTo(vertA.x, vertA.y) + lineTo(vertB.x, vertB.y) + lineTo(vertC.x, vertC.y) + close() + } + drawPath(path = trianglePath, color = Color.Gray, style = Stroke(width = 4f)) + + // Draw distinct corner nodes for visual reference + drawCircle(color = Color.Red, radius = 12f, center = vertA) // Vertex A + drawCircle(color = Color.Green, radius = 12f, center = vertB) // Vertex B + drawCircle(color = Color.Blue, radius = 12f, center = vertC) // Vertex C + + // 3. Compute Current 2D Handle Location from Weights (Forward Kinematics) + val handleX = weights.a * vertA.x + weights.b * vertB.x + weights.c * vertC.x + val handleY = weights.a * vertA.y + weights.b * vertB.y + weights.c * vertC.y + + // 4. Draw Interactive Selector Handle + drawCircle( + color = Color.DarkGray, + radius = 24f, + center = Offset(handleX, handleY), + style = Stroke(width = 6f) + ) + drawCircle( + color = Color.White, + radius = 20f, + center = Offset(handleX, handleY) + ) + } + } +} + +// Inverse Kinematics: Maps pixel offsets directly into normalized weights +private fun calculateWeightsFromScreenPos( + pointerPos: Offset, + canvasWidth: Float, + canvasHeight: Float +): SimplexWeights { + val centerX = canvasWidth / 2f + val centerY = canvasHeight / 2f + val radius = min(canvasWidth, canvasHeight) * 0.425f + val sqrt3Over2 = sqrt(3f) / 2f + + val ax = centerX + val ay = centerY - radius + val bx = centerX - radius * sqrt3Over2 + val by = centerY + radius * 0.5f + val cx = centerX + radius * sqrt3Over2 + val cy = centerY + radius * 0.5f + + // Determinant of the barycentric transformation matrix + val delta = (by - cy) * (ax - cx) + (cx - bx) * (ay - cy) + + // Compute raw weights using area cross products + var rawA = ((by - cy) * (pointerPos.x - cx) + (cx - bx) * (pointerPos.y - cy)) / delta + var rawB = ((cy - ay) * (pointerPos.x - cx) + (ax - cx) * (pointerPos.y - cy)) / delta + + // Clamp values inside [0.0, 1.0] boundaries + rawA = max(0f, min(1f, rawA)) + rawB = max(0f, min(1f, rawB)) + var rawC = max(0f, min(1f, 1f - rawA - rawB)) + + // Re-normalize weight vectors to eliminate tracking precision loss outside bounds + val sum = rawA + rawB + rawC + return if (sum > 0f) { + SimplexWeights(rawA / sum, rawB / sum, rawC / sum) + } else { + SimplexWeights(0.333f, 0.333f, 0.334f) + } +} + +@Preview(showBackground = true) +@Composable +fun SimplexTriangleEditorPreview() { + LearnGLESTheme { + SimplexTriangleEditor( + modifier = Modifier.fillMaxSize(), + initialWeights = SimplexWeights(0.333f, 0.333f, 0.334f), + onWeightsChanged = {} + ) + } +} From f11941c465081745a59684d8971dd41ed5ddb06d Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Tue, 2 Jun 2026 19:15:13 +0800 Subject: [PATCH 05/11] Implement dynamic lighting factor updates in BasicLightingScene Replaced hardcoded lighting constants with member variables and added logic to update ambient, diffuse, and specular factors from incoming event maps. --- .../main/cpp/lighting/BasicLightingScene.cpp | 29 +++++++++++++++++-- .../main/cpp/lighting/BasicLightingScene.h | 6 ++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp b/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp index 8303b1db..61e4959f 100644 --- a/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp +++ b/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp @@ -134,9 +134,9 @@ void BasicLightingScene::draw() { m_pLightingShader->setVec3("viewPos", m_camera->getPosition()); // light properties - m_pLightingShader->setVec3("light.ambient", 0.2f, 0.2f, 0.2f); - m_pLightingShader->setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); - m_pLightingShader->setVec3("light.specular", 1.0f, 1.0f, 1.0f); + m_pLightingShader->setVec3("light.ambient", ambientFactor, ambientFactor, ambientFactor); + m_pLightingShader->setVec3("light.diffuse", diffuseFactor, diffuseFactor, diffuseFactor); + m_pLightingShader->setVec3("light.specular", specularFactor, specularFactor, specularFactor); // material properties m_pLightingShader->setFloat("material.shininess", 64.f * 64.f); @@ -210,6 +210,29 @@ std::map BasicLightingScene::propertyEvent(std::mapsecond.type() == typeid(float)) { + ambientFactor = std::any_cast(it->second); + } else if (it->second.type() == typeid(double)) { + ambientFactor = (float)std::any_cast(it->second); + } + } + if (auto it = map.find("diffuseFactor"); it != map.end()) { + if (it->second.type() == typeid(float)) { + diffuseFactor = std::any_cast(it->second); + } else if (it->second.type() == typeid(double)) { + diffuseFactor = (float)std::any_cast(it->second); + } + } + if (auto it = map.find("specularFactor"); it != map.end()) { + if (it->second.type() == typeid(float)) { + specularFactor = std::any_cast(it->second); + } else if (it->second.type() == typeid(double)) { + specularFactor = (float)std::any_cast(it->second); + } + } + return {}; } diff --git a/tutorial/src/main/cpp/lighting/BasicLightingScene.h b/tutorial/src/main/cpp/lighting/BasicLightingScene.h index 7637857a..f663814b 100644 --- a/tutorial/src/main/cpp/lighting/BasicLightingScene.h +++ b/tutorial/src/main/cpp/lighting/BasicLightingScene.h @@ -36,6 +36,12 @@ public : unsigned int m_emissionMap = 0u; glm::vec3 m_lightPos; + //region Properties + float ambientFactor = 0.35f; + float diffuseFactor = 0.5f; + float specularFactor = 1.0f; + //endregion + void parseTargetCameraEvent(std::map &event); }; From 1696e5d933e5d6a03867f0a9633f14bc00bfdb8a Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Tue, 2 Jun 2026 19:27:44 +0800 Subject: [PATCH 06/11] Implement Blinn-Phong lighting toggle and refactor shader assets Add a UI toggle for Blinn-Phong lighting in `PhongVsBlinnActivity` and implement the corresponding event handling in the native `PhongVsBlinnScene`. - Add a "Blinn On" checkbox to the control panel and dispatch events via JNI - Implement `parseBlinnOnEvent` to update lighting state in `PhongVsBlinnScene` - Enable `GL_CULL_FACE` in the native OpenGL configuration - Rename `1.advanced_lighting` shader files to `advanced_lighting` - Refactor `ControlPanelContent` to support dynamic titles --- ...d_lighting.frag => advanced_lighting.frag} | 0 ...d_lighting.vert => advanced_lighting.vert} | 0 .../main/cpp/lighting/PhongVsBlinnScene.cpp | 50 +++++++++++-------- .../src/main/cpp/lighting/PhongVsBlinnScene.h | 11 ++-- .../lighting/PhongVsBlinnActivity.kt | 19 ++++++- 5 files changed, 56 insertions(+), 24 deletions(-) rename tutorial/src/main/assets/shaders/phong_blinn/{1.advanced_lighting.frag => advanced_lighting.frag} (100%) rename tutorial/src/main/assets/shaders/phong_blinn/{1.advanced_lighting.vert => advanced_lighting.vert} (100%) diff --git a/tutorial/src/main/assets/shaders/phong_blinn/1.advanced_lighting.frag b/tutorial/src/main/assets/shaders/phong_blinn/advanced_lighting.frag similarity index 100% rename from tutorial/src/main/assets/shaders/phong_blinn/1.advanced_lighting.frag rename to tutorial/src/main/assets/shaders/phong_blinn/advanced_lighting.frag diff --git a/tutorial/src/main/assets/shaders/phong_blinn/1.advanced_lighting.vert b/tutorial/src/main/assets/shaders/phong_blinn/advanced_lighting.vert similarity index 100% rename from tutorial/src/main/assets/shaders/phong_blinn/1.advanced_lighting.vert rename to tutorial/src/main/assets/shaders/phong_blinn/advanced_lighting.vert diff --git a/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.cpp b/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.cpp index 0dfc65fe..52d301e4 100644 --- a/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.cpp +++ b/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.cpp @@ -2,7 +2,7 @@ #include "Shader.h" #include "Camera.h" - #include "TargetCamera.h" +#include "TargetCamera.h" #include "Texture.h" PhongVsBlinnScene::PhongVsBlinnScene() { @@ -12,26 +12,27 @@ void PhongVsBlinnScene::init() { m_camera = new TargetCamera; // configure global opengl state // ----------------------------- + glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // build and compile our shader zprogram // ------------------------------------ - m_pShader = new Shader("shaders/phong_blinn/1.advanced_lighting.vert", - "shaders/phong_blinn/1.advanced_lighting.frag"); + m_pShader = new Shader("shaders/phong_blinn/advanced_lighting.vert", + "shaders/phong_blinn/advanced_lighting.frag"); // set up vertex data (and buffer(s)) and configure vertex attributes // ------------------------------------------------------------------ float planeVertices[] = { // positions // normals // texcoords - 10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 0.0f, - -10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - -10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 10.0f, + 10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 0.0f, + -10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 10.0f, - 10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 0.0f, - -10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 10.0f, - 10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 10.0f + 10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 0.0f, + -10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 10.0f, + 10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 10.0f }; // plane VAO glGenVertexArrays(1, &m_planeVAO); @@ -40,11 +41,11 @@ void PhongVsBlinnScene::init() { glBindBuffer(GL_ARRAY_BUFFER, m_planeVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(planeVertices), planeVertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) 0); glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) (3 * sizeof(float))); glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) (6 * sizeof(float))); glBindVertexArray(0); // load textures @@ -70,8 +71,7 @@ void PhongVsBlinnScene::draw() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // draw objects - if(m_pShader) - { + if (m_pShader) { m_pShader->use(); glm::mat4 projection = m_camera->getProjectionMatrix(); glm::mat4 view = m_camera->getViewMatrix(); @@ -92,8 +92,7 @@ void PhongVsBlinnScene::draw() { void PhongVsBlinnScene::destroy() { glDeleteVertexArrays(1, &m_planeVAO); glDeleteBuffers(1, &m_planeVBO); - if (m_pShader) - { + if (m_pShader) { delete m_pShader; m_pShader = nullptr; } @@ -111,17 +110,28 @@ std::map PhongVsBlinnScene::propertyEvent(std::map &event) { + if (auto it = event.find("on"); it != event.end()) { + if (it->second.type() == typeid(bool)) { + blinn = std::any_cast(it->second); + } + } +} + void PhongVsBlinnScene::parseTargetCameraEvent(std::map &event) { - auto* targetCamera = dynamic_cast(m_camera); + auto *targetCamera = dynamic_cast(m_camera); if (!targetCamera) return; if (auto it = event.find("single_touching"); it != event.end()) { if (it->second.type() == typeid(std::vector)) { - const auto& val = std::any_cast&>(it->second); + const auto &val = std::any_cast &>(it->second); if (val.size() >= 4) { targetCamera->onSingleTouching(glm::vec2(val[0], val[1]), glm::vec2(val[2], val[3])); } @@ -130,10 +140,10 @@ void PhongVsBlinnScene::parseTargetCameraEvent(std::map & if (auto it = event.find("double_touching"); it != event.end()) { if (it->second.type() == typeid(std::vector)) { - const auto& val = std::any_cast&>(it->second); + const auto &val = std::any_cast &>(it->second); if (val.size() >= 8) { targetCamera->onDoubleTouching(glm::vec2(val[0], val[1]), glm::vec2(val[2], val[3]), - glm::vec2(val[4], val[5]), glm::vec2(val[6], val[7])); + glm::vec2(val[4], val[5]), glm::vec2(val[6], val[7])); } } } diff --git a/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.h b/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.h index 71da98b8..eea1e2e5 100644 --- a/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.h +++ b/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.h @@ -22,6 +22,10 @@ public : std::map propertyEvent(std::map &map) override; + bool isBlinn() const; + + void setBlinn(bool blinn); + virtual ~PhongVsBlinnScene(); private: @@ -32,13 +36,14 @@ public : unsigned int m_floorTexture = 0u; glm::vec3 m_lightPos; + //region properties bool blinn = true; + //endregion + void parseTargetCameraEvent(std::map &event); -public: - bool isBlinn() const; - void setBlinn(bool blinn); + void parseBlinnOnEvent(std::map &event); }; #endif //LEARNGLES_PHONGVSBLINNSCENE_H diff --git a/tutorial/src/main/java/com/minininja/learngles/lighting/PhongVsBlinnActivity.kt b/tutorial/src/main/java/com/minininja/learngles/lighting/PhongVsBlinnActivity.kt index c59ba510..14435de0 100644 --- a/tutorial/src/main/java/com/minininja/learngles/lighting/PhongVsBlinnActivity.kt +++ b/tutorial/src/main/java/com/minininja/learngles/lighting/PhongVsBlinnActivity.kt @@ -23,6 +23,8 @@ import com.minininja.learngles.NativeHelper class PhongVsBlinnActivity : GLActivity() { private var active by mutableStateOf(true) + private var blinnOn by mutableStateOf(true) + override fun createTouchCallback(): Layer3DTouchCallback { return object : Layer3DTouchCallback { @@ -81,16 +83,31 @@ class PhongVsBlinnActivity : GLActivity() { @Composable override fun ControlPanel() { ControlPanelContent( + title = "Target Camera Events", active = active, onActiveChange = { active = it }, ) + + ControlPanelContent( + title = "Blinn On", + active = blinnOn, + onActiveChange = { + blinnOn = it + val event = mapOf("event_id" to "blinn_on_event", "on" to it) + glSurfaceView?.queueEvent { + NativeHelper.sendCommands(event) + glSurfaceView?.requestRender() + } + }, + ) } } @Composable private fun ControlPanelContent( + title: String, active: Boolean, onActiveChange: (Boolean) -> Unit, modifier: Modifier = Modifier, @@ -100,7 +117,7 @@ private fun ControlPanelContent( ) { Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier.padding(8.dp)) { - Text(text = "Target Camera Events", color = Color.White) + Text(text = title, color = Color.White) Checkbox( checked = active, onCheckedChange = onActiveChange From 0d7c90fb2b33cc86970b5ce0263adfdfdc15c19d Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Tue, 2 Jun 2026 20:30:08 +0800 Subject: [PATCH 07/11] Refactor lighting scene rendering and improve camera reset handling - Decoupled light cube rendering from the main lighting shader block in BasicLightingScene. - Added an explicit render request in PhongVsBlinnActivity when resetting the camera. - Updated PhongVsBlinnScene to use a texture helper and adjusted floor plane vertex data. - Initialized light position and cleaned up state configuration in PhongVsBlinnScene. --- .../main/cpp/lighting/BasicLightingScene.cpp | 34 ++++++++++--------- .../main/cpp/lighting/PhongVsBlinnScene.cpp | 24 ++++++------- .../lighting/PhongVsBlinnActivity.kt | 1 + 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp b/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp index 61e4959f..ee851949 100644 --- a/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp +++ b/tutorial/src/main/cpp/lighting/BasicLightingScene.cpp @@ -126,6 +126,10 @@ void BasicLightingScene::draw() { glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // view/projection transformations + glm::mat4 projection = m_camera->getProjectionMatrix(); + glm::mat4 view = m_camera->getViewMatrix(); + // be sure to activate shader when setting uniforms/drawing objects if(m_pLightingShader) { @@ -141,9 +145,7 @@ void BasicLightingScene::draw() { // material properties m_pLightingShader->setFloat("material.shininess", 64.f * 64.f); - // view/projection transformations - glm::mat4 projection = m_camera->getProjectionMatrix(); - glm::mat4 view = m_camera->getViewMatrix(); + m_pLightingShader->setMat4("projection", projection); m_pLightingShader->setMat4("view", view); @@ -164,20 +166,20 @@ void BasicLightingScene::draw() { // render the cube glBindVertexArray(m_cubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); + } - // also draw the lamp object - if (m_pLightCubeShader) { - m_pLightCubeShader->use(); - m_pLightCubeShader->setMat4("projection", projection); - m_pLightCubeShader->setMat4("view", view); - model = glm::mat4(1.0f); - model = glm::translate(model, m_lightPos); - model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube - m_pLightCubeShader->setMat4("model", model); - - glBindVertexArray(m_lightCubeVAO); - glDrawArrays(GL_TRIANGLES, 0, 36); - } + // also draw the lamp object + if (m_pLightCubeShader) { + m_pLightCubeShader->use(); + m_pLightCubeShader->setMat4("projection", projection); + m_pLightCubeShader->setMat4("view", view); + glm::mat model = glm::mat4(1.0f); + model = glm::translate(model, m_lightPos); + model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube + m_pLightCubeShader->setMat4("model", model); + + glBindVertexArray(m_lightCubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); } check_gl_error(); } diff --git a/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.cpp b/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.cpp index 52d301e4..a01b25b2 100644 --- a/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.cpp +++ b/tutorial/src/main/cpp/lighting/PhongVsBlinnScene.cpp @@ -10,12 +10,13 @@ PhongVsBlinnScene::PhongVsBlinnScene() { void PhongVsBlinnScene::init() { m_camera = new TargetCamera; + m_lightPos = glm::vec3(0.0f, 0.0f, 0.0f); // configure global opengl state // ----------------------------- glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +// glEnable(GL_BLEND); +// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // build and compile our shader zprogram // ------------------------------------ @@ -26,13 +27,13 @@ void PhongVsBlinnScene::init() { // ------------------------------------------------------------------ float planeVertices[] = { // positions // normals // texcoords - 10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 0.0f, - -10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - -10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 10.0f, + -10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + 10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 0.0f, + 10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 10.0f, - 10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 0.0f, - -10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 10.0f, - 10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 10.0f + -10.0f, -0.5f, 10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + 10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 10.0f, 10.0f, + -10.0f, -0.5f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 10.0f }; // plane VAO glGenVertexArrays(1, &m_planeVAO); @@ -54,8 +55,8 @@ void PhongVsBlinnScene::init() { // shader configuration // -------------------- - m_pShader->use(); - m_pShader->setInt("texture1", 0); +// m_pShader->use(); +// m_pShader->setInt("texture1", 0); } void PhongVsBlinnScene::resize(int width, int height) { @@ -83,8 +84,7 @@ void PhongVsBlinnScene::draw() { m_pShader->setInt("blinn", blinn); // floor glBindVertexArray(m_planeVAO); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, m_floorTexture); + m_pShader->setTexture("floorTexture", m_floorTexture, 0); glDrawArrays(GL_TRIANGLES, 0, 6); } } diff --git a/tutorial/src/main/java/com/minininja/learngles/lighting/PhongVsBlinnActivity.kt b/tutorial/src/main/java/com/minininja/learngles/lighting/PhongVsBlinnActivity.kt index 14435de0..ed061457 100644 --- a/tutorial/src/main/java/com/minininja/learngles/lighting/PhongVsBlinnActivity.kt +++ b/tutorial/src/main/java/com/minininja/learngles/lighting/PhongVsBlinnActivity.kt @@ -66,6 +66,7 @@ class PhongVsBlinnActivity : GLActivity() { val event = mapOf("event_id" to "target_camera_touching_event", "reset" to 1) NativeHelper.sendCommands(event) + glSurfaceView?.requestRender() Log.d("PhongVsBlinnActivity", "Custom DoubleClick at: $event") } From 1b5ede2442532553e5a94cf1c8246c52e886bc79 Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Tue, 2 Jun 2026 21:46:08 +0800 Subject: [PATCH 08/11] Implement dynamic lighting factor controls in LightCaster scene Add UI sliders to `LightCasterActivity` to control ambient, diffuse, and specular lighting factors and synchronize them with the native `LightCasterScene`. - Added state-backed sliders for ambient, diffuse, and specular factors in `LightCasterActivity`. - Implemented a reusable `FactorSlider` Composable with support for real-time updates and formatting. - Updated `LightCasterScene` to parse incoming factor events and apply values to shader uniforms. - Replaced hardcoded lighting constants in the C++ draw loop with dynamic member variables. - Added a Compose preview for the control panel UI. --- .../main/cpp/lighting/LightCasterScene.cpp | 29 ++++++- .../src/main/cpp/lighting/LightCasterScene.h | 6 ++ .../learngles/lighting/LightCasterActivity.kt | 77 ++++++++++++++++++- 3 files changed, 106 insertions(+), 6 deletions(-) diff --git a/tutorial/src/main/cpp/lighting/LightCasterScene.cpp b/tutorial/src/main/cpp/lighting/LightCasterScene.cpp index f49b680d..a35ea0ec 100644 --- a/tutorial/src/main/cpp/lighting/LightCasterScene.cpp +++ b/tutorial/src/main/cpp/lighting/LightCasterScene.cpp @@ -133,9 +133,9 @@ void LightCasterScene::draw() { m_pLightingShader->setVec3("viewPos", m_camera->getPosition()); // light properties - m_pLightingShader->setVec3("light.ambient", 0.35f, 0.35f, 0.35f); - m_pLightingShader->setVec3("light.diffuse", 0.8f, 0.8f, 0.8f); - m_pLightingShader->setVec3("light.specular", 1.0f, 1.0f, 1.0f); + m_pLightingShader->setVec3("light.ambient", ambientFactor, ambientFactor, ambientFactor); + m_pLightingShader->setVec3("light.diffuse", diffuseFactor, diffuseFactor, diffuseFactor); + m_pLightingShader->setVec3("light.specular", specularFactor, specularFactor, specularFactor); m_pLightingShader->setFloat("light.constant", 1.0f); m_pLightingShader->setFloat("light.linear", 0.09f); m_pLightingShader->setFloat("light.quadratic", 0.032f); @@ -204,6 +204,29 @@ std::map LightCasterScene::propertyEvent(std::mapsecond.type() == typeid(float)) { + ambientFactor = std::any_cast(it->second); + } else if (it->second.type() == typeid(double)) { + ambientFactor = (float)std::any_cast(it->second); + } + } + if (auto it = map.find("diffuseFactor"); it != map.end()) { + if (it->second.type() == typeid(float)) { + diffuseFactor = std::any_cast(it->second); + } else if (it->second.type() == typeid(double)) { + diffuseFactor = (float)std::any_cast(it->second); + } + } + if (auto it = map.find("specularFactor"); it != map.end()) { + if (it->second.type() == typeid(float)) { + specularFactor = std::any_cast(it->second); + } else if (it->second.type() == typeid(double)) { + specularFactor = (float)std::any_cast(it->second); + } + } + return {}; } diff --git a/tutorial/src/main/cpp/lighting/LightCasterScene.h b/tutorial/src/main/cpp/lighting/LightCasterScene.h index 5a48589e..82d403f2 100644 --- a/tutorial/src/main/cpp/lighting/LightCasterScene.h +++ b/tutorial/src/main/cpp/lighting/LightCasterScene.h @@ -36,6 +36,12 @@ public : unsigned int m_specularMap = 0u; glm::vec3 m_cubePositions[10]; + //region Properties + float ambientFactor = 0.35f; + float diffuseFactor = 0.5f; + float specularFactor = 1.0f; + //endregion + void parseTargetCameraEvent(std::map &event); }; diff --git a/tutorial/src/main/java/com/minininja/learngles/lighting/LightCasterActivity.kt b/tutorial/src/main/java/com/minininja/learngles/lighting/LightCasterActivity.kt index ae8bb27c..80076fd6 100644 --- a/tutorial/src/main/java/com/minininja/learngles/lighting/LightCasterActivity.kt +++ b/tutorial/src/main/java/com/minininja/learngles/lighting/LightCasterActivity.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material3.Checkbox +import androidx.compose.material3.Slider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -16,13 +17,19 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import androidx.compose.ui.tooling.preview.Preview import com.minininja.learngles.GLActivity import com.minininja.learngles.Layer3DTouchCallback import com.minininja.learngles.NativeHelper +import com.minininja.learngles.ui.theme.LearnGLESTheme class LightCasterActivity : GLActivity() { private var active by mutableStateOf(true) + private var ambientFactor by mutableStateOf(0.35f) + private var diffuseFactor by mutableStateOf(0.5f) + private var specularFactor by mutableStateOf(1.0f) + override fun createTouchCallback(): Layer3DTouchCallback { return object : Layer3DTouchCallback { @@ -85,26 +92,90 @@ class LightCasterActivity : GLActivity() { onActiveChange = { active = it }, + ambientFactor = ambientFactor, + onAmbientFactorChange = { + ambientFactor = it + updateProperty("ambientFactor", it) + }, + diffuseFactor = diffuseFactor, + onDiffuseFactorChange = { + diffuseFactor = it + updateProperty("diffuseFactor", it) + }, + specularFactor = specularFactor, + onSpecularFactorChange = { + specularFactor = it + updateProperty("specularFactor", it) + } ) } + + private fun updateProperty(name: String, value: Float) { + val event = mapOf(name to value) + NativeHelper.sendCommands(event) + glSurfaceView?.requestRender() + } } @Composable private fun ControlPanelContent( active: Boolean, onActiveChange: (Boolean) -> Unit, + ambientFactor: Float, + onAmbientFactorChange: (Float) -> Unit, + diffuseFactor: Float, + onDiffuseFactorChange: (Float) -> Unit, + specularFactor: Float, + onSpecularFactorChange: (Float) -> Unit, modifier: Modifier = Modifier, ) { Column( - modifier = modifier.wrapContentSize() + modifier = modifier.wrapContentSize().padding(8.dp) ) { - Row(verticalAlignment = Alignment.CenterVertically, - modifier = modifier.padding(8.dp)) { + Row(verticalAlignment = Alignment.CenterVertically) { Text(text = "Target Camera Events", color = Color.White) Checkbox( checked = active, onCheckedChange = onActiveChange ) } + + FactorSlider(label = "Ambient Factor", value = ambientFactor, onValueChange = onAmbientFactorChange) + FactorSlider(label = "Diffuse Factor", value = diffuseFactor, onValueChange = onDiffuseFactorChange) + FactorSlider(label = "Specular Factor", value = specularFactor, onValueChange = onSpecularFactorChange) + } +} + +@Composable +private fun FactorSlider( + label: String, + value: Float, + onValueChange: (Float) -> Unit, + modifier: Modifier = Modifier +) { + Column(modifier = modifier.padding(vertical = 4.dp)) { + Text(text = "$label: ${"%.2f".format(value)}", color = Color.White) + Slider( + value = value, + onValueChange = onValueChange, + valueRange = 0f..1f + ) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFF000000) +@Composable +private fun ControlPanelPreview() { + LearnGLESTheme { + ControlPanelContent( + active = true, + onActiveChange = {}, + ambientFactor = 0.35f, + onAmbientFactorChange = {}, + diffuseFactor = 0.5f, + onDiffuseFactorChange = {}, + specularFactor = 1.0f, + onSpecularFactorChange = {} + ) } } From 26e59237ba5aa0fef6ccdadd9797c148156c2121 Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Wed, 3 Jun 2026 08:09:16 +0800 Subject: [PATCH 09/11] Refactor shadow mapping implementation and simplify shader asset naming - Removed numeric prefixes from shader file names across `shadow`, `gamma_correction`, and `multiple_lights` directories. - Updated `ShadowScene` to track viewport dimensions and use them when resetting the viewport after the depth pass. - Improved OpenGL ES compatibility in `ShadowScene` by using `GL_DEPTH_COMPONENT24`, switching to `GL_CLAMP_TO_EDGE`, and adding framebuffer completion checks. - Enabled `GL_CULL_FACE` in `ShadowScene` and updated plane vertex data. - Fixed a shader calculation in `shadow_mapping.frag` by explicitly casting `textureSize` to `vec2`. - Adjusted ambient and light color constants in the shadow mapping fragment shader for better visual results. - Applied general code formatting and added property regions in `GammaCorrectionScene`. --- ..._correction.frag => gamma_correction.frag} | 0 ..._correction.vert => gamma_correction.vert} | 0 .../{6.light_cube.frag => light_cube.frag} | 0 .../{6.light_cube.vert => light_cube.vert} | 0 ...tiple_lights.frag => multiple_lights.frag} | 0 ...tiple_lights.vert => multiple_lights.vert} | 0 ...{3.1.3.debug_quad.vert => debug_quad.vert} | 0 ..._quad_depth.frag => debug_quad_depth.frag} | 0 ...hadow_mapping.frag => shadow_mapping.frag} | 6 +-- ...hadow_mapping.vert => shadow_mapping.vert} | 0 ...g_depth.frag => shadow_mapping_depth.frag} | 0 ...g_depth.vert => shadow_mapping_depth.vert} | 0 .../cpp/lighting/GammaCorrectionScene.cpp | 4 +- .../main/cpp/lighting/GammaCorrectionScene.h | 3 ++ .../main/cpp/lighting/MultiLightsScene.cpp | 6 ++- .../src/main/cpp/lighting/ShadowScene.cpp | 38 +++++++++++-------- tutorial/src/main/cpp/lighting/ShadowScene.h | 3 ++ 17 files changed, 38 insertions(+), 22 deletions(-) rename tutorial/src/main/assets/shaders/gamma_correction/{2.gamma_correction.frag => gamma_correction.frag} (100%) rename tutorial/src/main/assets/shaders/gamma_correction/{2.gamma_correction.vert => gamma_correction.vert} (100%) rename tutorial/src/main/assets/shaders/multiple_lights/{6.light_cube.frag => light_cube.frag} (100%) rename tutorial/src/main/assets/shaders/multiple_lights/{6.light_cube.vert => light_cube.vert} (100%) rename tutorial/src/main/assets/shaders/multiple_lights/{6.multiple_lights.frag => multiple_lights.frag} (100%) rename tutorial/src/main/assets/shaders/multiple_lights/{6.multiple_lights.vert => multiple_lights.vert} (100%) rename tutorial/src/main/assets/shaders/shadow/{3.1.3.debug_quad.vert => debug_quad.vert} (100%) rename tutorial/src/main/assets/shaders/shadow/{3.1.3.debug_quad_depth.frag => debug_quad_depth.frag} (100%) rename tutorial/src/main/assets/shaders/shadow/{3.1.3.shadow_mapping.frag => shadow_mapping.frag} (94%) rename tutorial/src/main/assets/shaders/shadow/{3.1.3.shadow_mapping.vert => shadow_mapping.vert} (100%) rename tutorial/src/main/assets/shaders/shadow/{3.1.3.shadow_mapping_depth.frag => shadow_mapping_depth.frag} (100%) rename tutorial/src/main/assets/shaders/shadow/{3.1.3.shadow_mapping_depth.vert => shadow_mapping_depth.vert} (100%) diff --git a/tutorial/src/main/assets/shaders/gamma_correction/2.gamma_correction.frag b/tutorial/src/main/assets/shaders/gamma_correction/gamma_correction.frag similarity index 100% rename from tutorial/src/main/assets/shaders/gamma_correction/2.gamma_correction.frag rename to tutorial/src/main/assets/shaders/gamma_correction/gamma_correction.frag diff --git a/tutorial/src/main/assets/shaders/gamma_correction/2.gamma_correction.vert b/tutorial/src/main/assets/shaders/gamma_correction/gamma_correction.vert similarity index 100% rename from tutorial/src/main/assets/shaders/gamma_correction/2.gamma_correction.vert rename to tutorial/src/main/assets/shaders/gamma_correction/gamma_correction.vert diff --git a/tutorial/src/main/assets/shaders/multiple_lights/6.light_cube.frag b/tutorial/src/main/assets/shaders/multiple_lights/light_cube.frag similarity index 100% rename from tutorial/src/main/assets/shaders/multiple_lights/6.light_cube.frag rename to tutorial/src/main/assets/shaders/multiple_lights/light_cube.frag diff --git a/tutorial/src/main/assets/shaders/multiple_lights/6.light_cube.vert b/tutorial/src/main/assets/shaders/multiple_lights/light_cube.vert similarity index 100% rename from tutorial/src/main/assets/shaders/multiple_lights/6.light_cube.vert rename to tutorial/src/main/assets/shaders/multiple_lights/light_cube.vert diff --git a/tutorial/src/main/assets/shaders/multiple_lights/6.multiple_lights.frag b/tutorial/src/main/assets/shaders/multiple_lights/multiple_lights.frag similarity index 100% rename from tutorial/src/main/assets/shaders/multiple_lights/6.multiple_lights.frag rename to tutorial/src/main/assets/shaders/multiple_lights/multiple_lights.frag diff --git a/tutorial/src/main/assets/shaders/multiple_lights/6.multiple_lights.vert b/tutorial/src/main/assets/shaders/multiple_lights/multiple_lights.vert similarity index 100% rename from tutorial/src/main/assets/shaders/multiple_lights/6.multiple_lights.vert rename to tutorial/src/main/assets/shaders/multiple_lights/multiple_lights.vert diff --git a/tutorial/src/main/assets/shaders/shadow/3.1.3.debug_quad.vert b/tutorial/src/main/assets/shaders/shadow/debug_quad.vert similarity index 100% rename from tutorial/src/main/assets/shaders/shadow/3.1.3.debug_quad.vert rename to tutorial/src/main/assets/shaders/shadow/debug_quad.vert diff --git a/tutorial/src/main/assets/shaders/shadow/3.1.3.debug_quad_depth.frag b/tutorial/src/main/assets/shaders/shadow/debug_quad_depth.frag similarity index 100% rename from tutorial/src/main/assets/shaders/shadow/3.1.3.debug_quad_depth.frag rename to tutorial/src/main/assets/shaders/shadow/debug_quad_depth.frag diff --git a/tutorial/src/main/assets/shaders/shadow/3.1.3.shadow_mapping.frag b/tutorial/src/main/assets/shaders/shadow/shadow_mapping.frag similarity index 94% rename from tutorial/src/main/assets/shaders/shadow/3.1.3.shadow_mapping.frag rename to tutorial/src/main/assets/shaders/shadow/shadow_mapping.frag index 5281e1c0..1ef02c17 100644 --- a/tutorial/src/main/assets/shaders/shadow/3.1.3.shadow_mapping.frag +++ b/tutorial/src/main/assets/shaders/shadow/shadow_mapping.frag @@ -33,7 +33,7 @@ float ShadowCalculation(vec4 fragPosLightSpace) // float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; // PCF float shadow = 0.0; - vec2 texelSize = 1.0 / textureSize(shadowMap, 0); + vec2 texelSize = 1.0 / vec2(textureSize(shadowMap, 0)); for(int x = -1; x <= 1; ++x) { for(int y = -1; y <= 1; ++y) @@ -55,9 +55,9 @@ void main() { vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb; vec3 normal = normalize(fs_in.Normal); - vec3 lightColor = vec3(0.3); + vec3 lightColor = vec3(1.0); // ambient - vec3 ambient = 0.3 * lightColor; + vec3 ambient = 0.15 * lightColor; // diffuse vec3 lightDir = normalize(lightPos - fs_in.FragPos); float diff = max(dot(lightDir, normal), 0.0); diff --git a/tutorial/src/main/assets/shaders/shadow/3.1.3.shadow_mapping.vert b/tutorial/src/main/assets/shaders/shadow/shadow_mapping.vert similarity index 100% rename from tutorial/src/main/assets/shaders/shadow/3.1.3.shadow_mapping.vert rename to tutorial/src/main/assets/shaders/shadow/shadow_mapping.vert diff --git a/tutorial/src/main/assets/shaders/shadow/3.1.3.shadow_mapping_depth.frag b/tutorial/src/main/assets/shaders/shadow/shadow_mapping_depth.frag similarity index 100% rename from tutorial/src/main/assets/shaders/shadow/3.1.3.shadow_mapping_depth.frag rename to tutorial/src/main/assets/shaders/shadow/shadow_mapping_depth.frag diff --git a/tutorial/src/main/assets/shaders/shadow/3.1.3.shadow_mapping_depth.vert b/tutorial/src/main/assets/shaders/shadow/shadow_mapping_depth.vert similarity index 100% rename from tutorial/src/main/assets/shaders/shadow/3.1.3.shadow_mapping_depth.vert rename to tutorial/src/main/assets/shaders/shadow/shadow_mapping_depth.vert diff --git a/tutorial/src/main/cpp/lighting/GammaCorrectionScene.cpp b/tutorial/src/main/cpp/lighting/GammaCorrectionScene.cpp index 97617a1d..aaccbef0 100644 --- a/tutorial/src/main/cpp/lighting/GammaCorrectionScene.cpp +++ b/tutorial/src/main/cpp/lighting/GammaCorrectionScene.cpp @@ -18,8 +18,8 @@ void GammaCorrectionScene::init() { // build and compile shaders // ------------------------- - m_pShader = new Shader("shaders/gamma_correction/2.gamma_correction.vert", - "shaders/gamma_correction/2.gamma_correction.frag"); + m_pShader = new Shader("shaders/gamma_correction/gamma_correction.vert", + "shaders/gamma_correction/gamma_correction.frag"); // set up vertex data (and buffer(s)) and configure vertex attributes // ------------------------------------------------------------------ diff --git a/tutorial/src/main/cpp/lighting/GammaCorrectionScene.h b/tutorial/src/main/cpp/lighting/GammaCorrectionScene.h index db1823ad..16caf6b4 100644 --- a/tutorial/src/main/cpp/lighting/GammaCorrectionScene.h +++ b/tutorial/src/main/cpp/lighting/GammaCorrectionScene.h @@ -32,7 +32,10 @@ public : glm::vec3 m_lightPositions[4]; glm::vec3 m_lightColors[4]; + + //region properties bool gammaEnabled = false; + //endregion void parseTargetCameraEvent(std::map &event); public: diff --git a/tutorial/src/main/cpp/lighting/MultiLightsScene.cpp b/tutorial/src/main/cpp/lighting/MultiLightsScene.cpp index 4740fe98..0b23944e 100644 --- a/tutorial/src/main/cpp/lighting/MultiLightsScene.cpp +++ b/tutorial/src/main/cpp/lighting/MultiLightsScene.cpp @@ -30,8 +30,10 @@ void MultiLightsScene::init() { // build and compile our shader zprogram // ------------------------------------ - m_pLightingShader = new Shader("shaders/multiple_lights/multiple_lights.vert", "shaders/multiple_lights/multiple_lights.frag"); - m_pLightCubeShader = new Shader("shaders/multiple_lights/light_cube.vert", "shaders/multiple_lights/light_cube.frag"); + m_pLightingShader = new Shader("shaders/multiple_lights/multiple_lights.vert", + "shaders/multiple_lights/multiple_lights.frag"); + m_pLightCubeShader = new Shader("shaders/multiple_lights/light_cube.vert", + "shaders/multiple_lights/light_cube.frag"); // set up vertex data (and buffer(s)) and configure vertex attributes // ------------------------------------------------------------------ diff --git a/tutorial/src/main/cpp/lighting/ShadowScene.cpp b/tutorial/src/main/cpp/lighting/ShadowScene.cpp index 12a53b8a..d984c028 100644 --- a/tutorial/src/main/cpp/lighting/ShadowScene.cpp +++ b/tutorial/src/main/cpp/lighting/ShadowScene.cpp @@ -13,24 +13,25 @@ void ShadowScene::init() { // configure global opengl state // ----------------------------- glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); // build and compile shaders // ------------------------- - m_pShader = new Shader("3.1.3.shadow_mapping.vert", "shaders/shadow/3.1.3.shadow_mapping.frag"); - m_pSimpleDepthShader = new Shader("shaders/shadow3.1.3.shadow_mapping_depth.vert", "shaders/shadow/3.1.3.shadow_mapping_depth.frag"); - m_pDebugDepthQuad = new Shader("shaders/shadow/3.1.3.debug_quad.vert", "shaders/shadow/3.1.3.debug_quad_depth.frag"); + m_pShader = new Shader("shaders/shadow/shadow_mapping.vert", "shaders/shadow/shadow_mapping.frag"); + m_pSimpleDepthShader = new Shader("shaders/shadow/shadow_mapping_depth.vert", "shaders/shadow/shadow_mapping_depth.frag"); + m_pDebugDepthQuad = new Shader("shaders/shadow/debug_quad.vert", "shaders/shadow/debug_quad_depth.frag"); // set up vertex data (and buffer(s)) and configure vertex attributes // ------------------------------------------------------------------ float planeVertices[] = { // positions // normals // texcoords - 25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 0.0f, -25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - -25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 25.0f, + 25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 0.0f, + 25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 25.0f, - 25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 0.0f, - -25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 25.0f, - 25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 25.0f + -25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + 25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 25.0f, + -25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 25.0f }; // plane VAO glGenVertexArrays(1, &m_planeVAO); @@ -56,18 +57,22 @@ void ShadowScene::init() { // create depth texture glGenTextures(1, &m_depthMap); glBindTexture(GL_TEXTURE_2D, m_depthMap); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - float borderColor[] = { 1.0, 1.0, 1.0, 1.0 }; - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // attach depth texture as FBO's depth buffer glBindFramebuffer(GL_FRAMEBUFFER, m_depthMapFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthMap, 0); - //glDrawBuffer(GL_NONE); //TODO Not support in GLSL + // glDrawBuffer(GL_NONE); //TODO Not support in GLSL + GLenum none = GL_NONE; + glDrawBuffers(1, &none); glReadBuffer(GL_NONE); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + LOGE("ShadowScene", "ERROR::FRAMEBUFFER:: Framebuffer is not complete!"); + glBindFramebuffer(GL_FRAMEBUFFER, 0); // shader configuration @@ -81,6 +86,8 @@ void ShadowScene::init() { void ShadowScene::resize(int width, int height) { m_camera->setAspec((float) width / (float) height); + m_width = width; + m_height = height; glViewport(0, 0, width, height); } @@ -111,7 +118,8 @@ void ShadowScene::draw() { } // reset viewport - glViewport(0, 0, 800, 600); + glViewport(0, 0, m_width, m_height); + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 2. render scene as normal using the generated depth/shadow map diff --git a/tutorial/src/main/cpp/lighting/ShadowScene.h b/tutorial/src/main/cpp/lighting/ShadowScene.h index 989efdad..8d0e7a74 100644 --- a/tutorial/src/main/cpp/lighting/ShadowScene.h +++ b/tutorial/src/main/cpp/lighting/ShadowScene.h @@ -40,6 +40,9 @@ public : unsigned int m_depthMapFBO = 0u; unsigned int m_depthMap = 0u; + int m_width = 0; + int m_height = 0; + void renderQuad(); void renderCube(); void renderScene(const Shader &shader); From 7bb26801ebf6deaac7abfaabc0854ed588a3060f Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Wed, 3 Jun 2026 11:04:11 +0800 Subject: [PATCH 10/11] Refactor HDR scene rendering and simplify camera interaction logic - Replace `TargetCamera` with a base `Camera` class and implement manual Z-axis movement based on touch input. - Rename HDR and lighting shader assets to remove numeric prefixes. - Update cube vertex data and normals to support rendering from an interior perspective. - Implement explicit `glViewport` calls when switching between the HDR framebuffer and the default back buffer. - Use `SCR_WIDTH` and `SCR_HEIGHT` constants for floating-point color buffer and depth buffer initialization. - Enable `GL_CULL_FACE` and adjust default light positions within the scene. - Add an explicit `requestRender()` call in `HdrActivity` when resetting the camera state. --- .../shaders/hdr/{6.hdr.frag => hdr.frag} | 0 .../shaders/hdr/{6.hdr.vert => hdr.vert} | 0 .../hdr/{6.lighting.frag => lighting.frag} | 0 .../hdr/{6.lighting.vert => lighting.vert} | 0 tutorial/src/main/cpp/lighting/HdrScene.cpp | 151 +++++++++--------- tutorial/src/main/cpp/lighting/HdrScene.h | 6 + .../learngles/lighting/HdrActivity.kt | 1 + 7 files changed, 82 insertions(+), 76 deletions(-) rename tutorial/src/main/assets/shaders/hdr/{6.hdr.frag => hdr.frag} (100%) rename tutorial/src/main/assets/shaders/hdr/{6.hdr.vert => hdr.vert} (100%) rename tutorial/src/main/assets/shaders/hdr/{6.lighting.frag => lighting.frag} (100%) rename tutorial/src/main/assets/shaders/hdr/{6.lighting.vert => lighting.vert} (100%) diff --git a/tutorial/src/main/assets/shaders/hdr/6.hdr.frag b/tutorial/src/main/assets/shaders/hdr/hdr.frag similarity index 100% rename from tutorial/src/main/assets/shaders/hdr/6.hdr.frag rename to tutorial/src/main/assets/shaders/hdr/hdr.frag diff --git a/tutorial/src/main/assets/shaders/hdr/6.hdr.vert b/tutorial/src/main/assets/shaders/hdr/hdr.vert similarity index 100% rename from tutorial/src/main/assets/shaders/hdr/6.hdr.vert rename to tutorial/src/main/assets/shaders/hdr/hdr.vert diff --git a/tutorial/src/main/assets/shaders/hdr/6.lighting.frag b/tutorial/src/main/assets/shaders/hdr/lighting.frag similarity index 100% rename from tutorial/src/main/assets/shaders/hdr/6.lighting.frag rename to tutorial/src/main/assets/shaders/hdr/lighting.frag diff --git a/tutorial/src/main/assets/shaders/hdr/6.lighting.vert b/tutorial/src/main/assets/shaders/hdr/lighting.vert similarity index 100% rename from tutorial/src/main/assets/shaders/hdr/6.lighting.vert rename to tutorial/src/main/assets/shaders/hdr/lighting.vert diff --git a/tutorial/src/main/cpp/lighting/HdrScene.cpp b/tutorial/src/main/cpp/lighting/HdrScene.cpp index 170330e3..1da99d37 100644 --- a/tutorial/src/main/cpp/lighting/HdrScene.cpp +++ b/tutorial/src/main/cpp/lighting/HdrScene.cpp @@ -10,15 +10,22 @@ HdrScene::HdrScene() { } void HdrScene::init() { - m_camera = new TargetCamera; + m_camera = new Camera; + m_cameraZ = 0.0f; + m_camera->setPosition(glm::vec3(0.0f, 0.0f, m_cameraZ)); + m_camera->setTargetPosition(glm::vec3(0.0f, 0.0f, -50.0f)); + m_camera->setUp(glm::vec3(0.0f, 1.0f, 0.0f)); + m_camera->start(); + // configure global opengl state // ----------------------------- + glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); // build and compile shaders // ------------------------- - m_pShader = new Shader("shaders/hdr/6.lighting.vert", "shaders/hdr/6.lighting.frag"); - m_pHdrShader = new Shader("shaders/hdr/6.hdr.vert", "shaders/hdr/6.hdr.frag"); + m_pShader = new Shader("shaders/hdr/lighting.vert", "shaders/hdr/lighting.frag"); + m_pHdrShader = new Shader("shaders/hdr/hdr.vert", "shaders/hdr/hdr.frag"); // load textures // ------------- @@ -30,13 +37,13 @@ void HdrScene::init() { // create floating point color buffer glGenTextures(1, &m_colorBuffer); glBindTexture(GL_TEXTURE_2D, m_colorBuffer); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 800, 600, 0, GL_RGBA, GL_FLOAT, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // create depth buffer (renderbuffer) glGenRenderbuffers(1, &m_rboDepth); glBindRenderbuffer(GL_RENDERBUFFER, m_rboDepth); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 800, 600); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT); // attach buffers glBindFramebuffer(GL_FRAMEBUFFER, m_hdrFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0); @@ -48,7 +55,7 @@ void HdrScene::init() { // lighting info // ------------- // positions - m_lightPositions.push_back(glm::vec3( 0.0f, 0.0f, 49.5f)); // back light + m_lightPositions.push_back(glm::vec3( 0.0f, 0.0f, 25.0f)); m_lightPositions.push_back(glm::vec3(-1.4f, -1.9f, 9.0f)); m_lightPositions.push_back(glm::vec3( 0.0f, -1.8f, 4.0f)); m_lightPositions.push_back(glm::vec3( 0.8f, -1.7f, 6.0f)); @@ -69,19 +76,24 @@ void HdrScene::init() { void HdrScene::resize(int width, int height) { m_camera->setAspec((float) width / (float) height); glViewport(0, 0, width, height); + m_width = width; + m_height = height; } void HdrScene::draw() { + m_camera->setPosition(glm::vec3(0.0f, 0.0f, m_cameraZ)); + m_camera->setTargetPosition(glm::vec3(0.0f, 0.0f, m_cameraZ - 1.0f)); m_camera->update(); // render // ------ - glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 1. render scene into floating point framebuffer // ----------------------------------------------- glBindFramebuffer(GL_FRAMEBUFFER, m_hdrFBO); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); glm::mat4 projection = m_camera->getProjectionMatrix(); glm::mat4 view = m_camera->getViewMatrix(); if(m_pShader) @@ -111,6 +123,7 @@ void HdrScene::draw() { // 2. now render floating point color buffer to 2D quad and tonemap HDR colors to default framebuffer's (clamped) color range // -------------------------------------------------------------------------------------------------------------------------- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, m_width, m_height); if(m_pHdrShader) { m_pHdrShader->use(); @@ -152,37 +165,23 @@ std::map HdrScene::propertyEvent(std::mapsecond.type() == typeid(std::string)) { auto eventIdStr = std::any_cast(eventIdIt->second); if ("target_camera_touching_event" == eventIdStr) { - parseTargetCameraEvent(map); + if (auto it = map.find("single_touching"); it != map.end()) { + if (it->second.type() == typeid(std::vector)) { + const auto& val = std::any_cast&>(it->second); + if (val.size() >= 4) { + float dy = val[3] - val[1]; + m_cameraZ -= dy * 0.05f; // Adjust sensitivity as needed + } + } + } } } return {}; } void HdrScene::parseTargetCameraEvent(std::map &event) { - auto* targetCamera = dynamic_cast(m_camera); - if (!targetCamera) return; - - if (auto it = event.find("single_touching"); it != event.end()) { - if (it->second.type() == typeid(std::vector)) { - const auto& val = std::any_cast&>(it->second); - if (val.size() >= 4) { - targetCamera->onSingleTouching(glm::vec2(val[0], val[1]), glm::vec2(val[2], val[3])); - } - } - } - - if (auto it = event.find("double_touching"); it != event.end()) { - if (it->second.type() == typeid(std::vector)) { - const auto& val = std::any_cast&>(it->second); - if (val.size() >= 8) { - targetCamera->onDoubleTouching(glm::vec2(val[0], val[1]), glm::vec2(val[2], val[3]), - glm::vec2(val[4], val[5]), glm::vec2(val[6], val[7])); - } - } - } - if (event.find("reset") != event.end()) { - targetCamera->reset(); + m_cameraZ = 0.0f; } } @@ -191,10 +190,10 @@ void HdrScene::renderQuad() { { float quadVertices[] = { // positions // texture Coords - -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, - 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, }; // setup plane VAO glGenVertexArrays(1, &m_quadVAO); @@ -217,48 +216,48 @@ void HdrScene::renderCube() { if (m_cubeVAO == 0) { float vertices[] = { - // back face - -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left - 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right - 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right - 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right - -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left - -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, // top-left - // front face - -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left - 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right - 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right - 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right - -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left - -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left - // left face - -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right - -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left - -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left - -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left - -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right - -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right - // right face - 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left - 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right - 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right - 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right - 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left - 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left - // bottom face - -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right - 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left - 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left - 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left - -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right - -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right - // top face - -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left - 1.0f, 1.0f , 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right - 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right - 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right - -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left - -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f // bottom-left + // back face (viewed from inside) + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left + 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right + -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left + // front face (viewed from inside) + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left + -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, // top-left + // left face (viewed from inside) + -1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right + -1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left + -1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left + -1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left + -1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right + -1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right + // right face (viewed from inside) + 1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left + 1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right + 1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right + 1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right + 1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left + 1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left + // top face (viewed from inside) + -1.0f, 1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-left + 1.0f, 1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-right + 1.0f, 1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-right + 1.0f, 1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-right + -1.0f, 1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-left + -1.0f, 1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-left + // bottom face (viewed from inside) + -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-right + 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-left + 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-left + 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-left + -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f // top-right + -1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // bottom-right }; glGenVertexArrays(1, &m_cubeVAO); glGenBuffers(1, &m_cubeVBO); diff --git a/tutorial/src/main/cpp/lighting/HdrScene.h b/tutorial/src/main/cpp/lighting/HdrScene.h index 26f64440..8744af38 100644 --- a/tutorial/src/main/cpp/lighting/HdrScene.h +++ b/tutorial/src/main/cpp/lighting/HdrScene.h @@ -25,6 +25,9 @@ public : virtual ~HdrScene(); private: + static constexpr unsigned int SCR_WIDTH = 800; + static constexpr unsigned int SCR_HEIGHT = 600; + void renderQuad(); void renderCube(); @@ -48,6 +51,9 @@ public : bool hdr = false; float exposure = 0.f; + float m_cameraZ = 0.0f; + int m_width = 0, m_height = 0; + void parseTargetCameraEvent(std::map &event); }; diff --git a/tutorial/src/main/java/com/minininja/learngles/lighting/HdrActivity.kt b/tutorial/src/main/java/com/minininja/learngles/lighting/HdrActivity.kt index 01e51f91..ab979f22 100644 --- a/tutorial/src/main/java/com/minininja/learngles/lighting/HdrActivity.kt +++ b/tutorial/src/main/java/com/minininja/learngles/lighting/HdrActivity.kt @@ -64,6 +64,7 @@ class HdrActivity : GLActivity() { val event = mapOf("event_id" to "target_camera_touching_event", "reset" to 1) NativeHelper.sendCommands(event) + glSurfaceView?.requestRender() Log.d("HdrActivity", "Custom DoubleClick at: $event") } From ef256f968060ba4da13b2e1e9737aeeee08a19ef Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Wed, 3 Jun 2026 13:07:40 +0800 Subject: [PATCH 11/11] Refactor Bloom and SSAO scenes with improved viewport handling and shader organization - Rename shader assets to remove numeric prefixes and update corresponding paths in `BloomScene` and `SsaoScene`. - Introduce `SCR_WIDTH` and `SCR_HEIGHT` constants for internal render buffer dimensions. - Implement explicit `glViewport` calls during multi-pass rendering to ensure correct scaling between internal buffers and the system framebuffer. - Update `BloomScene` vertex data for improved face orientation and enable `GL_CULL_FACE`. - Refactor SSAO shaders to use a uniform for `noiseScale` and fix an integer division bug in the occlusion calculation. - Optimize `blur.frag` by declaring weights as `const` and explicitly casting `textureSize` to `vec2`. - Initialize default light positions, colors, and exposure values across scenes. --- .../bloom/{7.bloom.frag => bloom.frag} | 0 .../bloom/{7.bloom.vert => bloom.vert} | 0 .../{7.bloom_final.frag => bloom_final.frag} | 0 .../{7.bloom_final.vert => bloom_final.vert} | 0 .../shaders/bloom/{7.blur.frag => blur.frag} | 4 +- .../shaders/bloom/{7.blur.vert => blur.vert} | 0 .../{7.light_box.frag => light_box.frag} | 0 .../shaders/ssao/{9.ssao.frag => ssao.frag} | 4 +- .../shaders/ssao/{9.ssao.vert => ssao.vert} | 0 .../ssao/{9.ssao_blur.frag => ssao_blur.frag} | 0 ....ssao_geometry.frag => ssao_geometry.frag} | 0 ....ssao_geometry.vert => ssao_geometry.vert} | 0 ....ssao_lighting.frag => ssao_lighting.frag} | 0 tutorial/src/main/cpp/lighting/BloomScene.cpp | 79 ++++++++++--------- tutorial/src/main/cpp/lighting/BloomScene.h | 6 +- tutorial/src/main/cpp/lighting/SsaoScene.cpp | 30 ++++--- tutorial/src/main/cpp/lighting/SsaoScene.h | 8 +- 17 files changed, 75 insertions(+), 56 deletions(-) rename tutorial/src/main/assets/shaders/bloom/{7.bloom.frag => bloom.frag} (100%) rename tutorial/src/main/assets/shaders/bloom/{7.bloom.vert => bloom.vert} (100%) rename tutorial/src/main/assets/shaders/bloom/{7.bloom_final.frag => bloom_final.frag} (100%) rename tutorial/src/main/assets/shaders/bloom/{7.bloom_final.vert => bloom_final.vert} (100%) rename tutorial/src/main/assets/shaders/bloom/{7.blur.frag => blur.frag} (81%) rename tutorial/src/main/assets/shaders/bloom/{7.blur.vert => blur.vert} (100%) rename tutorial/src/main/assets/shaders/bloom/{7.light_box.frag => light_box.frag} (100%) rename tutorial/src/main/assets/shaders/ssao/{9.ssao.frag => ssao.frag} (95%) rename tutorial/src/main/assets/shaders/ssao/{9.ssao.vert => ssao.vert} (100%) rename tutorial/src/main/assets/shaders/ssao/{9.ssao_blur.frag => ssao_blur.frag} (100%) rename tutorial/src/main/assets/shaders/ssao/{9.ssao_geometry.frag => ssao_geometry.frag} (100%) rename tutorial/src/main/assets/shaders/ssao/{9.ssao_geometry.vert => ssao_geometry.vert} (100%) rename tutorial/src/main/assets/shaders/ssao/{9.ssao_lighting.frag => ssao_lighting.frag} (100%) diff --git a/tutorial/src/main/assets/shaders/bloom/7.bloom.frag b/tutorial/src/main/assets/shaders/bloom/bloom.frag similarity index 100% rename from tutorial/src/main/assets/shaders/bloom/7.bloom.frag rename to tutorial/src/main/assets/shaders/bloom/bloom.frag diff --git a/tutorial/src/main/assets/shaders/bloom/7.bloom.vert b/tutorial/src/main/assets/shaders/bloom/bloom.vert similarity index 100% rename from tutorial/src/main/assets/shaders/bloom/7.bloom.vert rename to tutorial/src/main/assets/shaders/bloom/bloom.vert diff --git a/tutorial/src/main/assets/shaders/bloom/7.bloom_final.frag b/tutorial/src/main/assets/shaders/bloom/bloom_final.frag similarity index 100% rename from tutorial/src/main/assets/shaders/bloom/7.bloom_final.frag rename to tutorial/src/main/assets/shaders/bloom/bloom_final.frag diff --git a/tutorial/src/main/assets/shaders/bloom/7.bloom_final.vert b/tutorial/src/main/assets/shaders/bloom/bloom_final.vert similarity index 100% rename from tutorial/src/main/assets/shaders/bloom/7.bloom_final.vert rename to tutorial/src/main/assets/shaders/bloom/bloom_final.vert diff --git a/tutorial/src/main/assets/shaders/bloom/7.blur.frag b/tutorial/src/main/assets/shaders/bloom/blur.frag similarity index 81% rename from tutorial/src/main/assets/shaders/bloom/7.blur.frag rename to tutorial/src/main/assets/shaders/bloom/blur.frag index 25eb48a9..f085d3d2 100644 --- a/tutorial/src/main/assets/shaders/bloom/7.blur.frag +++ b/tutorial/src/main/assets/shaders/bloom/blur.frag @@ -7,11 +7,11 @@ in vec2 TexCoords; uniform sampler2D image; uniform bool horizontal; -uniform float weight[5] = float[] (0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162); +const float weight[5] = float[] (0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162); void main() { - vec2 tex_offset = 1.0 / textureSize(image, 0); // gets size of single texel + vec2 tex_offset = 1.0 / vec2(textureSize(image, 0)); // gets size of single texel vec3 result = texture(image, TexCoords).rgb * weight[0]; if(horizontal) { diff --git a/tutorial/src/main/assets/shaders/bloom/7.blur.vert b/tutorial/src/main/assets/shaders/bloom/blur.vert similarity index 100% rename from tutorial/src/main/assets/shaders/bloom/7.blur.vert rename to tutorial/src/main/assets/shaders/bloom/blur.vert diff --git a/tutorial/src/main/assets/shaders/bloom/7.light_box.frag b/tutorial/src/main/assets/shaders/bloom/light_box.frag similarity index 100% rename from tutorial/src/main/assets/shaders/bloom/7.light_box.frag rename to tutorial/src/main/assets/shaders/bloom/light_box.frag diff --git a/tutorial/src/main/assets/shaders/ssao/9.ssao.frag b/tutorial/src/main/assets/shaders/ssao/ssao.frag similarity index 95% rename from tutorial/src/main/assets/shaders/ssao/9.ssao.frag rename to tutorial/src/main/assets/shaders/ssao/ssao.frag index 381d3519..2079e9fc 100644 --- a/tutorial/src/main/assets/shaders/ssao/9.ssao.frag +++ b/tutorial/src/main/assets/shaders/ssao/ssao.frag @@ -16,7 +16,7 @@ float radius = 0.5; float bias = 0.025; // tile noise texture over screen based on screen dimensions divided by noise size -const vec2 noiseScale = vec2(800.0/4.0, 600.0/4.0); +uniform vec2 noiseScale; uniform mat4 projection; @@ -51,7 +51,7 @@ void main() float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth)); occlusion += (sampleDepth >= samplePos.z + bias ? 1.0 : 0.0) * rangeCheck; } - occlusion = 1.0 - (occlusion / kernelSize); + occlusion = 1.0 - occlusion / float(kernelSize); FragColor = occlusion; } diff --git a/tutorial/src/main/assets/shaders/ssao/9.ssao.vert b/tutorial/src/main/assets/shaders/ssao/ssao.vert similarity index 100% rename from tutorial/src/main/assets/shaders/ssao/9.ssao.vert rename to tutorial/src/main/assets/shaders/ssao/ssao.vert diff --git a/tutorial/src/main/assets/shaders/ssao/9.ssao_blur.frag b/tutorial/src/main/assets/shaders/ssao/ssao_blur.frag similarity index 100% rename from tutorial/src/main/assets/shaders/ssao/9.ssao_blur.frag rename to tutorial/src/main/assets/shaders/ssao/ssao_blur.frag diff --git a/tutorial/src/main/assets/shaders/ssao/9.ssao_geometry.frag b/tutorial/src/main/assets/shaders/ssao/ssao_geometry.frag similarity index 100% rename from tutorial/src/main/assets/shaders/ssao/9.ssao_geometry.frag rename to tutorial/src/main/assets/shaders/ssao/ssao_geometry.frag diff --git a/tutorial/src/main/assets/shaders/ssao/9.ssao_geometry.vert b/tutorial/src/main/assets/shaders/ssao/ssao_geometry.vert similarity index 100% rename from tutorial/src/main/assets/shaders/ssao/9.ssao_geometry.vert rename to tutorial/src/main/assets/shaders/ssao/ssao_geometry.vert diff --git a/tutorial/src/main/assets/shaders/ssao/9.ssao_lighting.frag b/tutorial/src/main/assets/shaders/ssao/ssao_lighting.frag similarity index 100% rename from tutorial/src/main/assets/shaders/ssao/9.ssao_lighting.frag rename to tutorial/src/main/assets/shaders/ssao/ssao_lighting.frag diff --git a/tutorial/src/main/cpp/lighting/BloomScene.cpp b/tutorial/src/main/cpp/lighting/BloomScene.cpp index 91349ca8..f18e4dbf 100644 --- a/tutorial/src/main/cpp/lighting/BloomScene.cpp +++ b/tutorial/src/main/cpp/lighting/BloomScene.cpp @@ -11,19 +11,20 @@ void BloomScene::init() { m_camera = new TargetCamera; // configure global opengl state // ----------------------------- + glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); // build and compile shaders // ------------------------- - m_pShader = new Shader("7.bloom.vert", "7.bloom.frag"); - m_pShaderLight = new Shader("7.bloom.vert", "7.light_box.frag"); - m_pShaderBlur = new Shader("7.blur.vert", "7.blur.frag"); - m_pShaderBloomFinal = new Shader("7.bloom_final.vert", "7.bloom_final.frag"); + m_pShader = new Shader("shaders/bloom/bloom.vert", "shaders/bloom/bloom.frag"); + m_pShaderLight = new Shader("shaders/bloom/bloom.vert", "shaders/bloom/light_box.frag"); + m_pShaderBlur = new Shader("shaders/bloom/blur.vert", "shaders/bloom/blur.frag"); + m_pShaderBloomFinal = new Shader("shaders/bloom/bloom_final.vert", "shaders/bloom/bloom_final.frag"); // load textures // ------------- - m_woodTexture = loadTexture("resources/textures/wood.png", true); // note that we're loading the texture as an SRGB texture - m_containerTexture = loadTexture("resources/textures/container2.png", true); // note that we're loading the texture as an SRGB texture + m_woodTexture = loadTexture("textures/wood.png", true); // note that we're loading the texture as an SRGB texture + m_containerTexture = loadTexture("textures/container2.png", true); // note that we're loading the texture as an SRGB texture // configure (floating point) framebuffers // --------------------------------------- @@ -34,7 +35,7 @@ void BloomScene::init() { for (unsigned int i = 0; i < 2; i++) { glBindTexture(GL_TEXTURE_2D, m_colorBuffers[i]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 800, 600, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // we clamp to the edge as the blur filter would otherwise sample repeated texture values! @@ -45,7 +46,7 @@ void BloomScene::init() { // create and attach depth buffer (renderbuffer) glGenRenderbuffers(1, &m_rboDepth); glBindRenderbuffer(GL_RENDERBUFFER, m_rboDepth); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 800, 600); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_rboDepth); // tell OpenGL which color attachments we'll use (of this framebuffer) for rendering unsigned int attachments[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; @@ -62,7 +63,7 @@ void BloomScene::init() { { glBindFramebuffer(GL_FRAMEBUFFER, m_pingpongFBO[i]); glBindTexture(GL_TEXTURE_2D, m_pingpongColorbuffers[i]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 800, 600, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // we clamp to the edge as the blur filter would otherwise sample repeated texture values! @@ -99,6 +100,8 @@ void BloomScene::init() { void BloomScene::resize(int width, int height) { m_camera->setAspec((float) width / (float) height); glViewport(0, 0, width, height); + m_width = width; + m_height = height; } void BloomScene::draw() { @@ -112,6 +115,7 @@ void BloomScene::draw() { // ----------------------------------------------- glBindFramebuffer(GL_FRAMEBUFFER, m_hdrFBO); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); glm::mat4 projection = m_camera->getProjectionMatrix(); glm::mat4 view = m_camera->getViewMatrix(); glm::mat4 model = glm::mat4(1.0f); @@ -217,6 +221,7 @@ void BloomScene::draw() { // 3. now render floating point color buffer to 2D quad and tonemap HDR colors to default framebuffer's (clamped) color range // -------------------------------------------------------------------------------------------------------------------------- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, m_width, m_height); if(m_pShaderBloomFinal) { m_pShaderBloomFinal->use(); @@ -317,10 +322,10 @@ void BloomScene::renderQuad() { { float quadVertices[] = { // positions // texture Coords - -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, - 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // Bottom Left + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // Bottom Right + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top Left + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // Top Right }; // setup plane VAO glGenVertexArrays(1, &m_quadVAO); @@ -343,48 +348,48 @@ void BloomScene::renderCube() { if (m_cubeVAO == 0) { float vertices[] = { - // back face + // back face (CCW viewed from back, so CW from front) -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left - 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right - 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right - 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right + 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, // top-left // front face -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left - 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right - 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right - 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left // left face -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right - -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left + -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left - -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right + -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right // right face - 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left - 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right - 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right - 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right - 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left - 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left + 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right + 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left // bottom face -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right - 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left - 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left - 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left - -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left + 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right + -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right // top face -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left - 1.0f, 1.0f , 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right - 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right - 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right - -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left - -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f // bottom-left + 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right + -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // bottom-left + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f // top-left }; glGenVertexArrays(1, &m_cubeVAO); glGenBuffers(1, &m_cubeVBO); diff --git a/tutorial/src/main/cpp/lighting/BloomScene.h b/tutorial/src/main/cpp/lighting/BloomScene.h index 483e8c0f..e7ff1c1f 100644 --- a/tutorial/src/main/cpp/lighting/BloomScene.h +++ b/tutorial/src/main/cpp/lighting/BloomScene.h @@ -24,6 +24,9 @@ public : virtual ~BloomScene(); + static constexpr unsigned int SCR_WIDTH = 800; + static constexpr unsigned int SCR_HEIGHT = 600; + private: void renderQuad(); void renderCube(); @@ -51,9 +54,10 @@ public : std::vector m_lightPositions; std::vector m_lightColors; + int m_width = 0, m_height = 0; bool bloom = false; - float exposure = 0.f; + float exposure = 1.0f; void parseTargetCameraEvent(std::map &event); }; diff --git a/tutorial/src/main/cpp/lighting/SsaoScene.cpp b/tutorial/src/main/cpp/lighting/SsaoScene.cpp index 99ee3c01..a43ede5e 100644 --- a/tutorial/src/main/cpp/lighting/SsaoScene.cpp +++ b/tutorial/src/main/cpp/lighting/SsaoScene.cpp @@ -19,10 +19,10 @@ void SsaoScene::init() { // build and compile shaders // ------------------------- - m_pShaderGeometryPass = new Shader("shaders/ssao/9.ssao_geometry.vert", "shaders/ssao/9.ssao_geometry.frag"); - m_pShaderLightingPass = new Shader("shaders/ssao/9.ssao.vert", "shaders/ssao/9.ssao_lighting.frag"); - m_pShaderSSAO = new Shader("shaders/ssao/9.ssao.vert", "shaders/ssao/9.ssao.frag"); - m_pShaderSSAOBlur = new Shader("shaders/ssao/9.ssao.vert", "shaders/ssao/9.ssao_blur.frag"); + m_pShaderGeometryPass = new Shader("shaders/ssao/ssao_geometry.vert", "shaders/ssao/ssao_geometry.frag"); + m_pShaderLightingPass = new Shader("shaders/ssao/ssao.vert", "shaders/ssao/ssao_lighting.frag"); + m_pShaderSSAO = new Shader("shaders/ssao/ssao.vert", "shaders/ssao/ssao.frag"); + m_pShaderSSAOBlur = new Shader("shaders/ssao/ssao.vert", "shaders/ssao/ssao_blur.frag"); // load models // ----------- @@ -35,7 +35,7 @@ void SsaoScene::init() { // position color buffer glGenTextures(1, &m_gPosition); glBindTexture(GL_TEXTURE_2D, m_gPosition); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 800, 600, 0, GL_RGBA, GL_FLOAT, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -44,14 +44,14 @@ void SsaoScene::init() { // normal color buffer glGenTextures(1, &m_gNormal); glBindTexture(GL_TEXTURE_2D, m_gNormal); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 800, 600, 0, GL_RGBA, GL_FLOAT, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_gNormal, 0); // color + specular color buffer glGenTextures(1, &m_gAlbedo); glBindTexture(GL_TEXTURE_2D, m_gAlbedo); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 800, 600, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, m_gAlbedo, 0); @@ -61,7 +61,7 @@ void SsaoScene::init() { // create and attach depth buffer (renderbuffer) glGenRenderbuffers(1, &m_rboDepth); glBindRenderbuffer(GL_RENDERBUFFER, m_rboDepth); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 800, 600); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_rboDepth); // finally check if framebuffer is complete if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) @@ -75,7 +75,7 @@ void SsaoScene::init() { // SSAO color buffer glGenTextures(1, &m_ssaoColorBuffer); glBindTexture(GL_TEXTURE_2D, m_ssaoColorBuffer); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 800, 600, 0, GL_RED, GL_FLOAT, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, SCR_WIDTH, SCR_HEIGHT, 0, GL_RED, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ssaoColorBuffer, 0); @@ -85,7 +85,7 @@ void SsaoScene::init() { glBindFramebuffer(GL_FRAMEBUFFER, m_ssaoBlurFBO); glGenTextures(1, &m_ssaoColorBufferBlur); glBindTexture(GL_TEXTURE_2D, m_ssaoColorBufferBlur); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 800, 600, 0, GL_RED, GL_FLOAT, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, SCR_WIDTH, SCR_HEIGHT, 0, GL_RED, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ssaoColorBufferBlur, 0); @@ -120,7 +120,7 @@ void SsaoScene::init() { } glGenTextures(1, &m_noiseTexture); glBindTexture(GL_TEXTURE_2D, m_noiseTexture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); @@ -142,6 +142,7 @@ void SsaoScene::init() { m_pShaderSSAO->setInt("gPosition", 0); m_pShaderSSAO->setInt("gNormal", 1); m_pShaderSSAO->setInt("texNoise", 2); + m_pShaderSSAO->setVec2("noiseScale", glm::vec2(SCR_WIDTH / 4.0f, SCR_HEIGHT / 4.0f)); } if(m_pShaderSSAOBlur) { @@ -153,6 +154,8 @@ void SsaoScene::init() { void SsaoScene::resize(int width, int height) { m_camera->setAspec((float) width / (float) height); glViewport(0, 0, width, height); + m_width = width; + m_height = height; } void SsaoScene::draw() { @@ -166,6 +169,7 @@ void SsaoScene::draw() { // ----------------------------------------------------------------- glBindFramebuffer(GL_FRAMEBUFFER, m_gBuffer); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); glm::mat4 projection = m_camera->getProjectionMatrix(); glm::mat4 view = m_camera->getViewMatrix(); glm::mat4 model = glm::mat4(1.0f); @@ -198,6 +202,7 @@ void SsaoScene::draw() { // ------------------------ glBindFramebuffer(GL_FRAMEBUFFER, m_ssaoFBO); glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); if(m_pShaderSSAO) { m_pShaderSSAO->use(); @@ -220,6 +225,7 @@ void SsaoScene::draw() { // ------------------------------------ glBindFramebuffer(GL_FRAMEBUFFER, m_ssaoBlurFBO); glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); if(m_pShaderSSAOBlur) { m_pShaderSSAOBlur->use(); @@ -233,6 +239,7 @@ void SsaoScene::draw() { // 4. lighting pass: traditional deferred Blinn-Phong lighting with added screen-space ambient occlusion // ----------------------------------------------------------------------------------------------------- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, m_width, m_height); if(m_pShaderLightingPass) { m_pShaderLightingPass->use(); @@ -307,7 +314,6 @@ void SsaoScene::destroy() { } SsaoScene::~SsaoScene() { - } std::map SsaoScene::propertyEvent(std::map &map) { diff --git a/tutorial/src/main/cpp/lighting/SsaoScene.h b/tutorial/src/main/cpp/lighting/SsaoScene.h index d2c1ce43..a8075cc9 100644 --- a/tutorial/src/main/cpp/lighting/SsaoScene.h +++ b/tutorial/src/main/cpp/lighting/SsaoScene.h @@ -12,6 +12,9 @@ class Model; class SsaoScene : public TutorialScene { public : + static constexpr int SCR_WIDTH = 800; + static constexpr int SCR_HEIGHT = 600; + SsaoScene(); void init() override; @@ -52,9 +55,10 @@ public : unsigned int m_cubeVBO = 0u; unsigned int m_quadVAO = 0u; unsigned int m_quadVBO = 0u; + int m_width = 0, m_height = 0; - glm::vec3 m_lightPos; - glm::vec3 m_lightColor; + glm::vec3 m_lightPos = glm::vec3(2.0, 4.0, -2.0); + glm::vec3 m_lightColor = glm::vec3(0.7 , 0.2, 0.7); void parseTargetCameraEvent(std::map &event); };