Skip to content

Commit 3a0984d

Browse files
benmotanotidoranteseduardo
authored andcommitted
chore: adjust masking (top, bottom)
1 parent a3be4b1 commit 3a0984d

8 files changed

Lines changed: 182 additions & 19 deletions

File tree

β€ŽViroRenderer/VROMaterial.cppβ€Ž

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -445,26 +445,50 @@ void VROMaterial::applySemanticMaskModifier() {
445445
"uniform highp float semantic_label_mask;",
446446
// Compute shared UV once. No Y-flip: gl_FragCoord and the transform are both GL-convention.
447447
"highp vec2 semUV = (ar_semantic_texture_transform * vec4(gl_FragCoord.xy / ar_viewport_size.xy, 0.0, 1.0)).xy;",
448-
"highp float label_raw = texture(semantic_texture, semUV).r * 255.0;",
449-
"int semLabel = int(floor(label_raw + 0.5));",
450-
"bool semMatches = (semLabel >= 1 && semLabel <= 11 && (int(semantic_label_mask) & (1 << semLabel)) != 0);",
448+
// Clamp inward by a small margin to skip the 1-2 pixel unlabeled border that ARCore's
449+
// neural network outputs at the left/right edges of the landscape image. In portrait mode
450+
// those edges map to thin horizontal lines at the top/bottom of the viewport. Without the
451+
// clamp those border pixels (label=0) cut the sky effect with a transparent stripe.
452+
// 0.005 β‰ˆ half a pixel on a 192-px-tall semantic texture; harmless for center content.
453+
"semUV = clamp(semUV, vec2(0.005), vec2(0.995));",
454+
"bool semInBounds = (semUV.x >= 0.0 && semUV.x <= 1.0 && semUV.y >= 0.0 && semUV.y <= 1.0);",
455+
"if (semInBounds) {",
456+
" highp float label_raw = texture(semantic_texture, semUV).r * 255.0;",
457+
" int semLabel = int(floor(label_raw + 0.5));",
458+
" bool semMatches = (semLabel >= 1 && semLabel <= 11 && (int(semantic_label_mask) & (1 << semLabel)) != 0);",
451459
// Debug mode (semantic_mask_mode == 2): colorise by label for overlay visualisation.
452460
// Blue = unlabeled, teal→orange gradient = classified pixels (fully opaque).
453-
"if (semantic_mask_mode >= 1.5) {",
454-
" highp float t = label_raw / 11.0;",
455-
" _output_color = vec4(t, float(semLabel > 0) * 0.8, 1.0 - t, 1.0);",
456-
"} else {",
461+
" if (semantic_mask_mode >= 1.5 && semantic_mask_mode < 2.5) {",
462+
" highp float t = label_raw / 11.0;",
463+
" _output_color = vec4(t, float(semLabel > 0) * 0.8, 1.0 - t, 1.0);",
464+
" } else {",
457465
// Confidence value from texture [0,1]. Used as alpha weight for soft edges.
458466
// On iOS (no platform confidence) the texture is 1x1 white β†’ conf = 1.0 β†’ hard edges.
459-
" highp float conf = texture(semantic_confidence_texture, semUV).r;",
467+
" highp float conf = texture(semantic_confidence_texture, semUV).r;",
460468
// ShowOnly (mode=0): show matched pixels with weight = conf; hide everything else.
461469
// Hide (mode=1): hide matched pixels; show others at full opacity.
462-
" if (semantic_mask_mode < 0.5) {",
463-
" _output_color.a *= semMatches ? conf : 0.0;",
464-
" } else {",
465-
" _output_color.a *= semMatches ? (1.0 - conf) : 1.0;",
470+
// ShowOnlySky (mode=3): like ShowOnly, but unlabeled pixels use viewport Y as a proxy
471+
// for sky context β€” upper half of screen shows sphere, lower half hides it. This
472+
// handles the ~10-15% border of the ARCore semantic image where the neural network
473+
// returns label=0 even though the camera sees real sky (or real ground). Regular
474+
// ShowOnly (mode=0) is unchanged so other semantic mask uses are not affected.
475+
" if (semantic_mask_mode < 0.5) {",
476+
" _output_color.a *= semMatches ? conf : 0.0;",
477+
" } else if (semantic_mask_mode > 2.5) {",
478+
" if (semMatches) {",
479+
" _output_color.a *= conf;",
480+
" } else if (semLabel == 0) {",
481+
" highp float normY = gl_FragCoord.y / ar_viewport_size.y;",
482+
" _output_color.a *= (normY > 0.5) ? 1.0 : 0.0;",
483+
" } else {",
484+
" _output_color.a *= 0.0;",
485+
" }",
486+
" } else {",
487+
" _output_color.a *= semMatches ? (1.0 - conf) : 1.0;",
488+
" }",
466489
" }",
467490
"}"
491+
// semInBounds == false: no semantic data available for this pixel β†’ pass through unchanged.
468492
};
469493

470494
_semanticMaskModifier = std::make_shared<VROShaderModifier>(

β€ŽViroRenderer/VROPortal.cppβ€Ž

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "VROBoundingBox.h"
3434
#include "VROPortalFrame.h"
3535
#include "VROShaderModifier.h"
36+
#include "VROSemantics.h"
3637

3738
// Parameters for sphere backgrounds
3839
static const float kSphereBackgroundRadius = 1;
@@ -391,6 +392,73 @@ void VROPortal::removeBackground() {
391392
_background.reset();
392393
}
393394

395+
#pragma mark - Sky Effect
396+
397+
void VROPortal::setSkyEffectBackground(std::shared_ptr<VROTexture> texture) {
398+
passert_thread(__func__);
399+
400+
// Remove previous sky effect node if present
401+
if (_skyEffectNode) {
402+
_skyEffectNode->removeFromParentNode();
403+
_skyEffectNode = nullptr;
404+
}
405+
406+
// Create sphere geometry (same radius/segments as the normal background sphere)
407+
std::shared_ptr<VROGeometry> sphere = VROSphere::createSphere(kSphereBackgroundRadius,
408+
kSphereBackgroundNumSegments,
409+
kSphereBackgroundNumSegments,
410+
false);
411+
sphere->setCameraEnclosure(true);
412+
sphere->setName("SkyEffectSphere");
413+
414+
std::shared_ptr<VROMaterial> material = sphere->getMaterials().front();
415+
material->setLightingModel(VROLightingModel::Constant);
416+
material->getDiffuse().setTexture(texture);
417+
material->setWritesToDepthBuffer(false);
418+
material->setReadsFromDepthBuffer(true);
419+
material->setNeedsToneMapping(false);
420+
// Alpha blending so the confidence-weighted mask fades at sky boundaries
421+
material->setBlendMode(VROBlendMode::Alpha);
422+
423+
// Semantic mask: ShowOnlySky (label 1 = Sky).
424+
// ShowOnlySky extends ShowOnly with asymmetric unlabeled handling: unlabeled pixels in
425+
// the upper screen half show the sphere (sky context), lower half hide it (ground context).
426+
// This covers the ~12% top/bottom strips where ARCore returns label=0 at image boundaries.
427+
// Set mode and mask BEFORE enabling β€” setSemanticMaskEnabled(true) calls
428+
// applySemanticMaskModifier() which captures the current mask/mode values
429+
// into shader uniform lambdas. Calling setSemanticLabelMask() afterwards
430+
// would not update the already-captured lambda.
431+
material->setSemanticMaskMode(VROSemanticMaskMode::ShowOnlySky);
432+
material->setSemanticLabelMask(1u << static_cast<int>(VROSemanticLabel::Sky));
433+
material->setSemanticMaskEnabled(true);
434+
435+
// Depth trick: push to NDC depth=0.9999 so 3D content (depth < 0.9999) renders on top
436+
if (!sPortalBackgroundShaderModifier) {
437+
std::vector<std::string> modifierCode = {
438+
"_vertex.position = _vertex.position.xyww;",
439+
"_vertex.position.z = _vertex.position.w * 0.9999;"
440+
};
441+
sPortalBackgroundShaderModifier = std::make_shared<VROShaderModifier>(VROShaderEntryPoint::Vertex,
442+
modifierCode);
443+
sPortalBackgroundShaderModifier->setName("portal_background");
444+
}
445+
material->addShaderModifier(sPortalBackgroundShaderModifier);
446+
447+
// Wrap in a node and attach as a child of this portal
448+
_skyEffectNode = std::make_shared<VRONode>();
449+
_skyEffectNode->setGeometry(sphere);
450+
_skyEffectNode->setName("SkyEffectNode");
451+
addChildNode(_skyEffectNode);
452+
}
453+
454+
void VROPortal::removeSkyEffectBackground() {
455+
passert_thread(__func__);
456+
if (_skyEffectNode) {
457+
_skyEffectNode->removeFromParentNode();
458+
_skyEffectNode = nullptr;
459+
}
460+
}
461+
394462
#pragma mark - Intersection
395463

396464
bool VROPortal::intersectsLineSegment(VROLineSegment segment) const {

β€ŽViroRenderer/VROPortal.hβ€Ž

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,16 @@ class VROPortal : public VRONode {
219219
return _background;
220220
}
221221
void removeBackground();
222+
223+
/*
224+
Set a sky-effect background: a sphere textured with the given texture, masked so that
225+
it only appears over pixels semantically labeled as Sky. Uses alpha blending via the
226+
confidence texture for smooth edges. Unlike setBackgroundSphere(), this sphere is added
227+
as a child node (not the portal background geometry) so that it participates in the
228+
scene's transparent render pass and the semantic mask shader is properly applied.
229+
*/
230+
void setSkyEffectBackground(std::shared_ptr<VROTexture> texture);
231+
void removeSkyEffectBackground();
222232

223233
private:
224234

@@ -278,6 +288,12 @@ class VROPortal : public VRONode {
278288
node content.
279289
*/
280290
std::shared_ptr<VROGeometry> _background;
291+
292+
/*
293+
Child node holding the sky-effect sphere geometry. Kept as a node so that semantic
294+
mask shader modifiers are applied normally via the scene render pass.
295+
*/
296+
std::shared_ptr<VRONode> _skyEffectNode;
281297

282298
/*
283299
The lighting environment for this portal. Determines the effect of image-based

β€ŽViroRenderer/VROSemantics.hβ€Ž

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,13 @@ enum class VROSemanticMode {
7272
* Controls whether fragments are shown or hidden based on label match.
7373
*/
7474
enum class VROSemanticMaskMode {
75-
ShowOnly = 0, // Only render fragments where the label matches
76-
Hide = 1, // Hide fragments where the label matches
77-
Debug = 2 // Color fragments by semantic label for debugging
75+
ShowOnly = 0, // Only render fragments where the label matches
76+
Hide = 1, // Hide fragments where the label matches
77+
Debug = 2, // Color fragments by semantic label for debugging
78+
ShowOnlySky = 3 // ShowOnly + unlabeled pixels use viewport Y to decide:
79+
// upper half β†’ show sphere (sky context),
80+
// lower half β†’ hide sphere (ground/foreground context).
81+
// Use this for Viro360Image skyEffect only.
7882
};
7983

8084
/*

β€ŽViroRenderer/capi/PortalScene_JNI.cppβ€Ž

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,32 @@ VRO_METHOD(void, nativeSetBackgroundCubeWithColor)(VRO_ARGS
177177
});
178178
}
179179

180+
VRO_METHOD(void, nativeSetSkyEffectTexture)(VRO_ARGS
181+
VRO_REF(VROPortal) portal_j,
182+
VRO_REF(VROTexture) texture_j) {
183+
std::weak_ptr<VROPortal> portal_w = VRO_REF_GET(VROPortal, portal_j);
184+
std::weak_ptr<VROTexture> texture_w = VRO_REF_GET(VROTexture, texture_j);
185+
186+
VROPlatformDispatchAsyncRenderer([portal_w, texture_w] {
187+
std::shared_ptr<VROPortal> portal = portal_w.lock();
188+
std::shared_ptr<VROTexture> texture = texture_w.lock();
189+
if (portal && texture) {
190+
portal->setSkyEffectBackground(texture);
191+
}
192+
});
193+
}
194+
195+
VRO_METHOD(void, nativeRemoveSkyEffect)(VRO_ARGS
196+
VRO_REF(VROPortal) portal_j) {
197+
std::weak_ptr<VROPortal> portal_w = VRO_REF_GET(VROPortal, portal_j);
198+
VROPlatformDispatchAsyncRenderer([portal_w] {
199+
std::shared_ptr<VROPortal> portal = portal_w.lock();
200+
if (portal) {
201+
portal->removeSkyEffectBackground();
202+
}
203+
});
204+
}
205+
180206
VRO_METHOD(void, nativeSetLightingEnvironment)(VRO_ARGS
181207
VRO_REF(VROPortal) portal_j,
182208
VRO_REF(VROTexture) texture_j) {

β€Žandroid/sharedCode/src/main/cpp/arcore/VROARSessionARCore.cppβ€Ž

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2669,11 +2669,12 @@ static void uploadR8Texture(std::shared_ptr<VROTexture> &texture,
26692669
std::weak_ptr<VRODriver> driverWeak) {
26702670
if (!texture || texture->getWidth() != width || texture->getHeight() != height) {
26712671
auto vData = std::make_shared<VROData>((void *)data, dataLength, VRODataOwnership::Copy);
2672+
std::vector<std::shared_ptr<VROData>> dataVec{ vData };
2673+
std::vector<uint32_t> mipSizes;
26722674
texture = std::make_shared<VROTexture>(
26732675
VROTextureType::Texture2D, VROTextureFormat::R8, VROTextureInternalFormat::R8,
26742676
false, VROMipmapMode::None,
2675-
std::vector<std::shared_ptr<VROData>>{ vData },
2676-
width, height, std::vector<uint32_t>());
2677+
dataVec, width, height, mipSizes);
26772678
texture->setMinificationFilter(VROFilterMode::Nearest);
26782679
texture->setMagnificationFilter(VROFilterMode::Nearest);
26792680
texture->setWrapS(VROWrapMode::Clamp);

β€Žandroid/sharedCode/src/main/java/com/viro/core/PortalScene.javaβ€Ž

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,27 @@ public Texture getLightingEnvironment() {
262262
return mLightingEnvironment;
263263
}
264264

265+
/**
266+
* Set the sky-effect background for this PortalScene. The provided {@link Texture} will
267+
* be rendered as a sphere that is only visible over pixels semantically labeled as sky by
268+
* ARCore scene semantics. The sphere uses alpha blending via the confidence texture for
269+
* smooth sky/non-sky boundary transitions.
270+
* <p>
271+
* Requires ARCore scene semantics to be enabled on the AR session.
272+
*
273+
* @param texture The spherical {@link Texture} to display over sky pixels.
274+
*/
275+
public void setSkyEffectTexture(Texture texture) {
276+
nativeSetSkyEffectTexture(mNativeRef, texture.mNativeRef);
277+
}
278+
279+
/**
280+
* Remove the sky-effect background previously set via {@link #setSkyEffectTexture(Texture)}.
281+
*/
282+
public void removeSkyEffect() {
283+
nativeRemoveSkyEffect(mNativeRef);
284+
}
285+
265286
private native long nativeCreatePortalScene();
266287
private native long nativeCreatePortalDelegate();
267288
private native long nativeDestroyPortalScene(long nativeRef);
@@ -275,6 +296,8 @@ private native void nativeSetBackgroundRotation(long nativeRef, float radiansX,
275296
private native long nativeAttachDelegate(long nativeRef, long delegateRef);
276297
private native void nativeSetPassable(long nativeRef, boolean passable);
277298
private native void nativeSetPortalEntrance(long nativeRef, long portalNativeRef);
299+
private native void nativeSetSkyEffectTexture(long nativeRef, long textureRef);
300+
private native void nativeRemoveSkyEffect(long nativeRef);
278301

279302
/*
280303
Invoked from JNI.

β€Žios/ViroKit/VROARSessioniOS.cppβ€Ž

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2640,11 +2640,12 @@ std::shared_ptr<VROTexture> VROARSessioniOS::getSemanticConfidenceTexture() {
26402640
if (!_defaultConfidenceTexture) {
26412641
static const uint8_t white = 255;
26422642
auto data = std::make_shared<VROData>((void *)&white, 1, VRODataOwnership::Copy);
2643+
std::vector<std::shared_ptr<VROData>> dataVec{ data };
2644+
std::vector<uint32_t> mipSizes;
26432645
_defaultConfidenceTexture = std::make_shared<VROTexture>(
26442646
VROTextureType::Texture2D, VROTextureFormat::R8, VROTextureInternalFormat::R8,
26452647
false, VROMipmapMode::None,
2646-
std::vector<std::shared_ptr<VROData>>{ data },
2647-
1, 1, std::vector<uint32_t>());
2648+
dataVec, 1, 1, mipSizes);
26482649
_defaultConfidenceTexture->setMinificationFilter(VROFilterMode::Nearest);
26492650
_defaultConfidenceTexture->setMagnificationFilter(VROFilterMode::Nearest);
26502651
_defaultConfidenceTexture->setWrapS(VROWrapMode::Clamp);

0 commit comments

Comments
Β (0)