mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
fix(globe): Navara overlay colours are sRGB
Review of Tasks 4-5: the overlay pass encodes linear to sRGB, and the instance and line colours went in as if linear: grey 0.5 drew 187 (Cesium 128) and [1, 0.6, 0.2] drew 255/203/124 (Cesium 255/153/52). The sprite, point and line shaders now decode them (sRGBTransferEOTF), as the sRGB atlas texture already was. A contract check draws mid-tone sprites, points and lines (grey 0.5 and [1, 0.6, 0.2]) and reads their centres: within 3 per channel on every engine. Navara 79/79.
This commit is contained in:
1 parent
645f042450
commit
68ff4e1c25
3 files changed
+105
-3
No files matched your search
@@ -90,7 +90,8 @@ float gevTactical() {
|
||||
const FRAGMENT_COLOR = /* glsl */ `
|
||||
#include <color_fragment>
|
||||
if (gevOverlayHidden()) discard;
|
||||
diffuseColor = uGevColor;
|
||||
// sRGB colours (as Cesium takes them) into the linear output.
|
||||
diffuseColor = sRGBTransferEOTF(uGevColor);
|
||||
diffuseColor.a *= uGevAlphaScale;
|
||||
if (uGevDash > 1.5) {
|
||||
diffuseColor.a *= gevTactical();
|
||||
|
||||
@@ -89,6 +89,9 @@ varying float vGevOutline;
|
||||
${OVERLAY_DEPTH_FRAGMENT}
|
||||
`;
|
||||
|
||||
// Instance colours are sRGB (as Cesium takes them) and the overlay output
|
||||
// encodes linear to sRGB, so they are decoded first (the atlas is an sRGB
|
||||
// texture, decoded by the sampler): grey 0.5 draws 128, not 187.
|
||||
const fragmentColor = (point) =>
|
||||
point
|
||||
? /* glsl */ `
|
||||
@@ -96,13 +99,13 @@ const fragmentColor = (point) =>
|
||||
if (gevOverlayHidden()) discard;
|
||||
float gevRadius = length(vGevLocal) * 2.0;
|
||||
if (gevRadius > 1.0) discard;
|
||||
diffuseColor *= vGevColor;
|
||||
diffuseColor *= sRGBTransferEOTF(vGevColor);
|
||||
if (gevRadius > 1.0 - vGevOutline) diffuseColor.rgb = vec3(0.0);
|
||||
`
|
||||
: /* glsl */ `
|
||||
#include <color_fragment>
|
||||
if (gevOverlayHidden()) discard;
|
||||
diffuseColor *= vGevColor;
|
||||
diffuseColor *= sRGBTransferEOTF(vGevColor);
|
||||
`;
|
||||
|
||||
// Overlay uniforms of a mesh drawn outside an overlay (tests): no depth.
|
||||
|
||||
@@ -591,6 +591,104 @@ export function createContractSuite(
|
||||
);
|
||||
},
|
||||
],
|
||||
[
|
||||
'colours: mid-tone sprite, point and line colours draw as asked (within 3 per channel)',
|
||||
async () => {
|
||||
if (!readRegion || !makeCanvas) return;
|
||||
await nadirView(1e6);
|
||||
const at = (lon, lat, height = 20_000) =>
|
||||
cartesian3.fromDegrees(lon, lat, height);
|
||||
const GREY = [0.5, 0.5, 0.5, 1];
|
||||
const ORANGE = [1, 0.6, 0.2, 1];
|
||||
const atlasCanvas = makeCanvas(16, 16);
|
||||
const context = atlasCanvas.getContext('2d');
|
||||
context.fillStyle = '#ffffff';
|
||||
context.fillRect(0, 0, 16, 16);
|
||||
const sprites = engine.movers.createSet({
|
||||
id: 'contract-colour-sprites',
|
||||
kind: 'sprite',
|
||||
capacity: 2,
|
||||
order: 100,
|
||||
depthTest: 'always',
|
||||
sprite: {
|
||||
atlas: { canvas: atlasCanvas, cells: { box: [0, 0, 1, 1] } },
|
||||
sizePx: 32,
|
||||
},
|
||||
});
|
||||
const points = engine.movers.createSet({
|
||||
id: 'contract-colour-points',
|
||||
kind: 'point',
|
||||
capacity: 2,
|
||||
order: 100,
|
||||
depthTest: 'always',
|
||||
point: { sizePx: 32 },
|
||||
});
|
||||
const lines = [GREY, ORANGE].map((color, index) =>
|
||||
engine.lines.createLine({
|
||||
id: `contract-colour-line-${index}`,
|
||||
widthPx: 16,
|
||||
color,
|
||||
}),
|
||||
);
|
||||
// The centre 4×4 pixels' mean, as 0..255 per channel.
|
||||
const centre = (position) => {
|
||||
const screen = engine.projector.worldToWindow(position);
|
||||
const data = readRegion(
|
||||
Math.round(screen.x) - 2,
|
||||
Math.round(screen.y) - 2,
|
||||
4,
|
||||
4,
|
||||
);
|
||||
const sum = [0, 0, 0];
|
||||
for (let i = 0; i < data.length; i += 4)
|
||||
for (let k = 0; k < 3; k++) sum[k] += data[i + k];
|
||||
return sum.map((value) => value / (data.length / 4));
|
||||
};
|
||||
const cases = [
|
||||
['sprite', GREY, at(19.5, 10.5)],
|
||||
['sprite', ORANGE, at(20.5, 10.5)],
|
||||
['point', GREY, at(19.5, 9.5)],
|
||||
['point', ORANGE, at(20.5, 9.5)],
|
||||
['line', GREY, at(19.5, 10)],
|
||||
['line', ORANGE, at(20.5, 10)],
|
||||
];
|
||||
try {
|
||||
sprites.set(0, { position: cases[0][2], color: GREY, cell: 'box' });
|
||||
sprites.set(1, { position: cases[1][2], color: ORANGE, cell: 'box' });
|
||||
sprites.setCount(2);
|
||||
sprites.commit();
|
||||
points.set(0, { position: cases[2][2], color: GREY });
|
||||
points.set(1, { position: cases[3][2], color: ORANGE });
|
||||
points.setCount(2);
|
||||
points.commit();
|
||||
// Short vertical lines through their sample points.
|
||||
lines[0].setPositions([at(19.5, 9.9), at(19.5, 10.1)]);
|
||||
lines[1].setPositions([at(20.5, 9.9), at(20.5, 10.1)]);
|
||||
const off = (seen, colour) =>
|
||||
Math.max(
|
||||
...seen.map((value, k) => Math.abs(value - colour[k] * 255)),
|
||||
);
|
||||
await until(
|
||||
() => cases.every(([, colour, p]) => off(centre(p), colour) <= 3),
|
||||
'mid-tone colours within 3',
|
||||
);
|
||||
} catch (error) {
|
||||
const seen = cases
|
||||
.map(
|
||||
([kind, colour, p]) =>
|
||||
`${kind} ${colour.slice(0, 3)} → ${centre(p)
|
||||
.map((v) => v.toFixed(0))
|
||||
.join('/')}`,
|
||||
)
|
||||
.join('; ');
|
||||
throw new ContractError(`${error.message}: ${seen}`);
|
||||
} finally {
|
||||
sprites.destroy();
|
||||
points.destroy();
|
||||
for (const line of lines) line.destroy();
|
||||
}
|
||||
},
|
||||
],
|
||||
[
|
||||
'lines: a line draws between its projections, turns with its model matrix and dims where hidden',
|
||||
async () => {
|
||||
|
||||
Reference in new issue
Block a user