From 0cd41e98143863eaebc44f1f255a072bd7b0cf18 Mon Sep 17 00:00:00 2001 From: Ole-Morten Duesund Date: Thu, 29 Jan 2026 17:07:44 +0100 Subject: [PATCH] Fix front camera rotation mirroring in shader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add uIsFrontCamera uniform to shader and adjust coordinate transformations for front camera's mirrored texture coordinates: - Position transform: (1-posY, 1-posX) instead of (posY, 1-posX) - Angle transform: -angle - 90° instead of +angle + 90° Applied to linearFocusDistance, radialFocusDistance, and blur direction calculations in sampleBlurred. Co-Authored-By: Claude Opus 4.5 --- .../tiltshift/effect/TiltShiftRenderer.kt | 2 +- .../naiv/tiltshift/effect/TiltShiftShader.kt | 5 +- app/src/main/res/raw/tiltshift_fragment.glsl | 61 ++++++++++++++----- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt b/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt index 09a5bf7..cd1873c 100644 --- a/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt +++ b/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt @@ -126,7 +126,7 @@ class TiltShiftRenderer( GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT) // Use shader and set parameters - shader.use(cameraTextureId, blurParameters, surfaceWidth, surfaceHeight) + shader.use(cameraTextureId, blurParameters, surfaceWidth, surfaceHeight, isFrontCamera) // Set vertex positions GLES20.glEnableVertexAttribArray(shader.aPositionLocation) diff --git a/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftShader.kt b/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftShader.kt index 2dc8447..6b962b5 100644 --- a/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftShader.kt +++ b/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftShader.kt @@ -24,6 +24,7 @@ class TiltShiftShader(private val context: Context) { // Uniform locations private var uTextureLocation: Int = 0 private var uModeLocation: Int = 0 + private var uIsFrontCameraLocation: Int = 0 private var uAngleLocation: Int = 0 private var uPositionXLocation: Int = 0 private var uPositionYLocation: Int = 0 @@ -65,6 +66,7 @@ class TiltShiftShader(private val context: Context) { // Get uniform locations uTextureLocation = GLES20.glGetUniformLocation(programId, "uTexture") uModeLocation = GLES20.glGetUniformLocation(programId, "uMode") + uIsFrontCameraLocation = GLES20.glGetUniformLocation(programId, "uIsFrontCamera") uAngleLocation = GLES20.glGetUniformLocation(programId, "uAngle") uPositionXLocation = GLES20.glGetUniformLocation(programId, "uPositionX") uPositionYLocation = GLES20.glGetUniformLocation(programId, "uPositionY") @@ -82,7 +84,7 @@ class TiltShiftShader(private val context: Context) { /** * Uses the shader program and sets uniforms. */ - fun use(textureId: Int, params: BlurParameters, width: Int, height: Int) { + fun use(textureId: Int, params: BlurParameters, width: Int, height: Int, isFrontCamera: Boolean = false) { GLES20.glUseProgram(programId) // Bind camera texture @@ -92,6 +94,7 @@ class TiltShiftShader(private val context: Context) { // Set effect parameters GLES20.glUniform1i(uModeLocation, if (params.mode == BlurMode.RADIAL) 1 else 0) + GLES20.glUniform1i(uIsFrontCameraLocation, if (isFrontCamera) 1 else 0) GLES20.glUniform1f(uAngleLocation, params.angle) GLES20.glUniform1f(uPositionXLocation, params.positionX) GLES20.glUniform1f(uPositionYLocation, params.positionY) diff --git a/app/src/main/res/raw/tiltshift_fragment.glsl b/app/src/main/res/raw/tiltshift_fragment.glsl index 86c4d99..db1b415 100644 --- a/app/src/main/res/raw/tiltshift_fragment.glsl +++ b/app/src/main/res/raw/tiltshift_fragment.glsl @@ -10,6 +10,7 @@ uniform samplerExternalOES uTexture; // Effect parameters uniform int uMode; // 0 = linear, 1 = radial +uniform int uIsFrontCamera; // 0 = back camera, 1 = front camera uniform float uAngle; // Rotation angle in radians uniform float uPositionX; // Horizontal center of focus (0-1) uniform float uPositionY; // Vertical center of focus (0-1) @@ -24,9 +25,15 @@ varying vec2 vTexCoord; // Calculate signed distance from the focus region for LINEAR mode float linearFocusDistance(vec2 uv) { // Center point of the focus region - // Transform from screen coordinates to texture coordinates (90° rotation) - // Screen (x,y) -> Texture (y, 1-x) - vec2 center = vec2(uPositionY, 1.0 - uPositionX); + // Transform from screen coordinates to texture coordinates + // Back camera: Screen (x,y) -> Texture (y, 1-x) + // Front camera: Screen (x,y) -> Texture (1-y, 1-x) (additional X flip for mirror) + vec2 center; + if (uIsFrontCamera == 1) { + center = vec2(1.0 - uPositionY, 1.0 - uPositionX); + } else { + center = vec2(uPositionY, 1.0 - uPositionX); + } vec2 offset = uv - center; // Correct for screen aspect ratio to make coordinate space square @@ -35,9 +42,15 @@ float linearFocusDistance(vec2 uv) { float screenAspect = uResolution.x / uResolution.y; offset.y *= screenAspect; - // Adjust angle by +90 degrees to compensate for the coordinate transformation - // The position transform is a 90° CW rotation, so angles transform as θ + 90° - float adjustedAngle = uAngle + 1.5707963; + // Adjust angle to compensate for the coordinate transformation + // Back camera: +90° for the 90° CW rotation + // Front camera: -90° (negated due to X flip mirror effect) + float adjustedAngle; + if (uIsFrontCamera == 1) { + adjustedAngle = -uAngle - 1.5707963; + } else { + adjustedAngle = uAngle + 1.5707963; + } float cosA = cos(adjustedAngle); float sinA = sin(adjustedAngle); @@ -50,9 +63,13 @@ float linearFocusDistance(vec2 uv) { // Calculate signed distance from the focus region for RADIAL mode float radialFocusDistance(vec2 uv) { // Center point of the focus region - // Transform from screen coordinates to texture coordinates (90° rotation) - // Screen (x,y) -> Texture (y, 1-x) - vec2 center = vec2(uPositionY, 1.0 - uPositionX); + // Transform from screen coordinates to texture coordinates + vec2 center; + if (uIsFrontCamera == 1) { + center = vec2(1.0 - uPositionY, 1.0 - uPositionX); + } else { + center = vec2(uPositionY, 1.0 - uPositionX); + } vec2 offset = uv - center; // Correct for screen aspect ratio to make coordinate space square @@ -61,9 +78,13 @@ float radialFocusDistance(vec2 uv) { float screenAspect = uResolution.x / uResolution.y; offset.y *= screenAspect; - // Apply rotation - // Adjust angle by +90 degrees to compensate for the coordinate transformation - float adjustedAngle = uAngle + 1.5707963; + // Apply rotation with angle adjustment for coordinate transformation + float adjustedAngle; + if (uIsFrontCamera == 1) { + adjustedAngle = -uAngle - 1.5707963; + } else { + adjustedAngle = uAngle + 1.5707963; + } float cosA = cos(adjustedAngle); float sinA = sin(adjustedAngle); vec2 rotated = vec2( @@ -120,8 +141,13 @@ vec4 sampleBlurred(vec2 uv, float blur) { vec2 blurDir; if (uMode == 1) { // Radial: blur away from center - // Transform from screen coordinates to texture coordinates (90° rotation) - vec2 center = vec2(uPositionY, 1.0 - uPositionX); + // Transform from screen coordinates to texture coordinates + vec2 center; + if (uIsFrontCamera == 1) { + center = vec2(1.0 - uPositionY, 1.0 - uPositionX); + } else { + center = vec2(uPositionY, 1.0 - uPositionX); + } vec2 toCenter = uv - center; float len = length(toCenter); if (len > 0.001) { @@ -132,7 +158,12 @@ vec4 sampleBlurred(vec2 uv, float blur) { } else { // Linear: blur perpendicular to focus line // Adjust angle for coordinate transformation - float blurAngle = uAngle + 1.5707963; + float blurAngle; + if (uIsFrontCamera == 1) { + blurAngle = -uAngle - 1.5707963; + } else { + blurAngle = uAngle + 1.5707963; + } blurDir = vec2(cos(blurAngle), sin(blurAngle)); }