mirror of
https://github.com/mapillary/mapillary-js.git
synced 2026-02-01 14:33:45 +00:00
feat: viewer api to set texture shader globally
This commit is contained in:
parent
dd9505bbc4
commit
86f26fc05b
134
examples/debug/shader.html
Normal file
134
examples/debug/shader.html
Normal file
@ -0,0 +1,134 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MapillaryJS Shader</title>
|
||||
<link rel="icon" href="data:," />
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
|
||||
<link rel="stylesheet" href="/dist/mapillary.css" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.viewer {
|
||||
width: 100%;
|
||||
height: calc(100% - 32px);
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 4px;
|
||||
margin-left: 6px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="module">
|
||||
import {
|
||||
CameraControls,
|
||||
Shader,
|
||||
ShaderChunk,
|
||||
Viewer,
|
||||
} from "/dist/mapillary.module.js";
|
||||
import { accessToken } from "/doc-src/.access-token/token.js";
|
||||
|
||||
(function main() {
|
||||
const imageId = "303729947926829";
|
||||
|
||||
const container = document.createElement("div");
|
||||
container.className = "viewer";
|
||||
document.body.append(container);
|
||||
|
||||
const viewer = new Viewer({
|
||||
accessToken,
|
||||
cameraControls: CameraControls.Street,
|
||||
component: { cover: false },
|
||||
container,
|
||||
});
|
||||
|
||||
viewer.moveTo(imageId).catch((error) => console.warn(error));
|
||||
addButtons(viewer);
|
||||
})();
|
||||
|
||||
function addButton(content, handler) {
|
||||
const button = document.createElement("button");
|
||||
button.textContent = content;
|
||||
button.addEventListener("click", handler);
|
||||
document.body.append(button);
|
||||
}
|
||||
|
||||
function addButtons(viewer) {
|
||||
addButton("Texture", () => viewer.setShader(Shader.texture));
|
||||
addButton("Magnesis", () => viewer.setShader(makeMagnesis()));
|
||||
addButton("Earth", () =>
|
||||
viewer.setCameraControls(CameraControls.Earth)
|
||||
);
|
||||
addButton("Street", () =>
|
||||
viewer.setCameraControls(CameraControls.Street)
|
||||
);
|
||||
}
|
||||
|
||||
function makeMagnesis() {
|
||||
const vertex = /* glsl */ `
|
||||
#include <uniforms_vertex>
|
||||
#include <varyings_vertex>
|
||||
|
||||
varying vec4 cameraCoords;
|
||||
|
||||
void main()
|
||||
{
|
||||
cameraCoords = modelViewMatrix * vec4(position, 1.0);
|
||||
#include <extrinsic_vertex>
|
||||
#include <gl_position_vertex>
|
||||
}
|
||||
`;
|
||||
const fragment = `
|
||||
#include <precision_fragment>
|
||||
#include <common>
|
||||
#include <uniforms_fragment>
|
||||
#include <varyings_fragment>
|
||||
#include <coordinates>
|
||||
|
||||
#expand <parameters>
|
||||
#expand <uniforms>
|
||||
#expand <project_to_sfm_definition>
|
||||
|
||||
varying vec4 cameraCoords;
|
||||
|
||||
void main()
|
||||
{
|
||||
#include <bearing_fragment>
|
||||
#expand <project_to_sfm_invocation>
|
||||
#include <map_color_fragment>
|
||||
|
||||
float magnesis = cameraCoords.x * 3.0;
|
||||
if (abs(magnesis - floor(magnesis)) < 0.07) {
|
||||
mapColor.r = 1.0;
|
||||
}
|
||||
|
||||
#include <gl_frag_color_fragment>
|
||||
}
|
||||
`;
|
||||
const magnesis = {
|
||||
fragment,
|
||||
vertex,
|
||||
};
|
||||
return magnesis;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -48,6 +48,7 @@ import { ComponentConfiguration } from "../interfaces/ComponentConfiguration";
|
||||
import { Transform } from "../../geo/Transform";
|
||||
import { ComponentName } from "../ComponentName";
|
||||
import { State } from "../../state/State";
|
||||
import { GLShader } from "../../shader/Shader";
|
||||
|
||||
interface ImageGLRendererOperation {
|
||||
(renderer: ImageGLRenderer): ImageGLRenderer;
|
||||
@ -170,10 +171,11 @@ export class ImageComponent extends Component<ComponentConfiguration> {
|
||||
.subscribe(this._rendererOperation$));
|
||||
|
||||
subs.push(this._navigator.stateService.currentState$.pipe(
|
||||
withLatestFrom(this._navigator.projectionService.shader$),
|
||||
map(
|
||||
(frame: AnimationFrame): ImageGLRendererOperation => {
|
||||
([frame, shader]: [AnimationFrame, GLShader]): ImageGLRendererOperation => {
|
||||
return (renderer: ImageGLRenderer): ImageGLRenderer => {
|
||||
renderer.updateFrame(frame);
|
||||
renderer.updateFrame(frame, shader);
|
||||
|
||||
return renderer;
|
||||
};
|
||||
@ -381,10 +383,11 @@ export class ImageComponent extends Component<ComponentConfiguration> {
|
||||
share());
|
||||
|
||||
subs.push(cachedPanNodes$.pipe(
|
||||
withLatestFrom(this._navigator.projectionService.shader$),
|
||||
map(
|
||||
([n, t]: [ImageNode, Transform]): ImageGLRendererOperation => {
|
||||
([[n, t], s]: [[ImageNode, Transform], GLShader]): ImageGLRendererOperation => {
|
||||
return (renderer: ImageGLRenderer): ImageGLRenderer => {
|
||||
renderer.addPeripheryPlane(n, t);
|
||||
renderer.addPeripheryPlane(n, t, s);
|
||||
|
||||
return renderer;
|
||||
};
|
||||
|
||||
@ -9,6 +9,7 @@ import { TextureProvider } from "../../tile/TextureProvider";
|
||||
import { MeshFactory } from "../util/MeshFactory";
|
||||
import { MeshScene } from "../util/MeshScene";
|
||||
import { ProjectorShaderMaterial } from "./interfaces/ProjectorShaderMaterial";
|
||||
import { GLShader } from "../../shader/Shader";
|
||||
|
||||
export class ImageGLRenderer {
|
||||
private _factory: MeshFactory;
|
||||
@ -53,8 +54,8 @@ export class ImageGLRenderer {
|
||||
this._needsRender = true;
|
||||
}
|
||||
|
||||
public addPeripheryPlane(image: Image, transform: Transform): void {
|
||||
const mesh: THREE.Mesh = this._factory.createMesh(image, transform);
|
||||
public addPeripheryPlane(image: Image, transform: Transform, shader: GLShader): void {
|
||||
const mesh: THREE.Mesh = this._factory.createMesh(image, transform, shader);
|
||||
const planes: { [key: string]: THREE.Mesh; } = {};
|
||||
planes[image.id] = mesh;
|
||||
this._scene.addPeripheryPlanes(planes);
|
||||
@ -68,11 +69,11 @@ export class ImageGLRenderer {
|
||||
this._needsRender = true;
|
||||
}
|
||||
|
||||
public updateFrame(frame: AnimationFrame): void {
|
||||
public updateFrame(frame: AnimationFrame, shader: GLShader): void {
|
||||
this._updateFrameId(frame.id);
|
||||
this._needsRender = this._updateAlpha(frame.state.alpha) || this._needsRender;
|
||||
this._needsRender = this._updateAlphaOld(frame.state.alpha) || this._needsRender;
|
||||
this._needsRender = this._updateImagePlanes(frame.state) || this._needsRender;
|
||||
this._needsRender = this._updateImagePlanes(frame.state, shader) || this._needsRender;
|
||||
}
|
||||
|
||||
public setTextureProvider(key: string, provider: TextureProvider): void {
|
||||
@ -129,7 +130,7 @@ export class ImageGLRenderer {
|
||||
const plane: THREE.Mesh = planes[key];
|
||||
|
||||
let material: ProjectorShaderMaterial = <ProjectorShaderMaterial>plane.material;
|
||||
let texture: THREE.Texture = <THREE.Texture>material.uniforms.projectorTex.value;
|
||||
let texture: THREE.Texture = <THREE.Texture>material.uniforms.map.value;
|
||||
|
||||
texture.image = imageElement;
|
||||
texture.needsUpdate = true;
|
||||
@ -228,7 +229,7 @@ export class ImageGLRenderer {
|
||||
return true;
|
||||
}
|
||||
|
||||
private _updateImagePlanes(state: IAnimationState): boolean {
|
||||
private _updateImagePlanes(state: IAnimationState, shader: GLShader): boolean {
|
||||
if (state.currentImage == null ||
|
||||
state.currentImage.id === this._currentKey) {
|
||||
return false;
|
||||
@ -250,7 +251,10 @@ export class ImageGLRenderer {
|
||||
if (previousKey != null) {
|
||||
if (previousKey !== this._currentKey && previousKey !== this._previousKey) {
|
||||
let previousMesh: THREE.Mesh =
|
||||
this._factory.createMesh(state.previousImage, state.previousTransform);
|
||||
this._factory.createMesh(
|
||||
state.previousImage,
|
||||
state.previousTransform,
|
||||
shader);
|
||||
|
||||
const previousPlanes: { [key: string]: THREE.Mesh; } = {};
|
||||
previousPlanes[previousKey] = previousMesh;
|
||||
@ -264,7 +268,8 @@ export class ImageGLRenderer {
|
||||
let currentMesh =
|
||||
this._factory.createMesh(
|
||||
state.currentImage,
|
||||
state.currentTransform);
|
||||
state.currentTransform,
|
||||
shader);
|
||||
|
||||
const planes: { [key: string]: THREE.Mesh; } = {};
|
||||
planes[currentKey] = currentMesh;
|
||||
@ -288,11 +293,11 @@ export class ImageGLRenderer {
|
||||
const plane: THREE.Mesh = planes[key];
|
||||
let material: ProjectorShaderMaterial = <ProjectorShaderMaterial>plane.material;
|
||||
|
||||
let oldTexture: THREE.Texture = <THREE.Texture>material.uniforms.projectorTex.value;
|
||||
material.uniforms.projectorTex.value = null;
|
||||
let oldTexture: THREE.Texture = <THREE.Texture>material.uniforms.map.value;
|
||||
material.uniforms.map.value = null;
|
||||
oldTexture.dispose();
|
||||
|
||||
material.uniforms.projectorTex.value = texture;
|
||||
material.uniforms.map.value = texture;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,11 +3,11 @@ import * as THREE from "three";
|
||||
import { Transform } from "../../geo/Transform";
|
||||
import { Image } from "../../graph/Image";
|
||||
import { isFisheye, isSpherical } from "../../geo/Geo";
|
||||
import { IUniform, Matrix3, Matrix4, Vector2, Vector3, Vector4 } from "three";
|
||||
import { IUniform, Material, Matrix3, Matrix4, ShaderMaterial, Vector2, Vector3, Vector4 } from "three";
|
||||
import { Camera } from "../../geometry/Camera";
|
||||
|
||||
import { resolveExpands, resolveIncludes } from "../../shader/Resolver";
|
||||
import { Shader } from "../../shader/Shader";
|
||||
import { GLShader } from "../../shader/Shader";
|
||||
|
||||
function makeCameraUniforms(camera: Camera): { [key: string]: IUniform; } {
|
||||
const cameraUniforms: { [key: string]: IUniform; } = {};
|
||||
@ -79,60 +79,60 @@ export class MeshFactory {
|
||||
this._imageSphereRadius = imageSphereRadius != null ? imageSphereRadius : 200;
|
||||
}
|
||||
|
||||
public createMesh(image: Image, transform: Transform): THREE.Mesh {
|
||||
public createMesh(image: Image, transform: Transform, shader: GLShader): THREE.Mesh {
|
||||
const texture = this._createTexture(image.image);
|
||||
const materialParameters =
|
||||
this._createMaterialParameters(
|
||||
transform,
|
||||
texture,
|
||||
shader);
|
||||
const material = new THREE.ShaderMaterial(materialParameters);
|
||||
|
||||
if (isSpherical(transform.cameraType)) {
|
||||
return this._createImageSphere(image, transform);
|
||||
return this._createImageSphere(image, transform, material);
|
||||
} else if (isFisheye(transform.cameraType)) {
|
||||
return this._createImagePlaneFisheye(image, transform);
|
||||
return this._createImagePlaneFisheye(image, transform, material);
|
||||
} else {
|
||||
return this._createImagePlane(image, transform);
|
||||
return this._createImagePlane(image, transform, material);
|
||||
}
|
||||
}
|
||||
|
||||
private _createImageSphere(image: Image, transform: Transform): THREE.Mesh {
|
||||
let texture: THREE.Texture = this._createTexture(image.image);
|
||||
let materialParameters: THREE.ShaderMaterialParameters = this._createDefaultMaterialParameters(transform, texture);
|
||||
let material: THREE.ShaderMaterial = new THREE.ShaderMaterial(materialParameters);
|
||||
|
||||
let mesh: THREE.Mesh = this._useMesh(transform, image) ?
|
||||
new THREE.Mesh(this._getImageSphereGeo(transform, image), material) :
|
||||
new THREE.Mesh(this._getFlatImageSphereGeo(transform), material);
|
||||
|
||||
return mesh;
|
||||
private _createImageSphere(
|
||||
image: Image,
|
||||
transform: Transform,
|
||||
material: ShaderMaterial): THREE.Mesh {
|
||||
const geometry = this._useMesh(transform, image) ?
|
||||
this._getImageSphereGeo(transform, image) :
|
||||
this._getFlatImageSphereGeo(transform);
|
||||
return new THREE.Mesh(geometry, material);
|
||||
}
|
||||
|
||||
private _createImagePlane(image: Image, transform: Transform): THREE.Mesh {
|
||||
let texture: THREE.Texture = this._createTexture(image.image);
|
||||
let materialParameters: THREE.ShaderMaterialParameters = this._createDefaultMaterialParameters(transform, texture);
|
||||
let material: THREE.ShaderMaterial = new THREE.ShaderMaterial(materialParameters);
|
||||
|
||||
let geometry: THREE.BufferGeometry = this._useMesh(transform, image) ?
|
||||
private _createImagePlane(
|
||||
image: Image,
|
||||
transform: Transform,
|
||||
material: ShaderMaterial): THREE.Mesh {
|
||||
const geometry = this._useMesh(transform, image) ?
|
||||
this._getImagePlaneGeo(transform, image) :
|
||||
this._getRegularFlatImagePlaneGeo(transform);
|
||||
|
||||
return new THREE.Mesh(geometry, material);
|
||||
}
|
||||
|
||||
private _createImagePlaneFisheye(image: Image, transform: Transform): THREE.Mesh {
|
||||
let texture: THREE.Texture = this._createTexture(image.image);
|
||||
let materialParameters: THREE.ShaderMaterialParameters = this._createDefaultMaterialParameters(transform, texture);
|
||||
let material: THREE.ShaderMaterial = new THREE.ShaderMaterial(materialParameters);
|
||||
|
||||
let geometry: THREE.BufferGeometry = this._useMesh(transform, image) ?
|
||||
private _createImagePlaneFisheye(image: Image, transform: Transform, material: ShaderMaterial): THREE.Mesh {
|
||||
const geometry = this._useMesh(transform, image) ?
|
||||
this._getImagePlaneGeoFisheye(transform, image) :
|
||||
this._getRegularFlatImagePlaneGeoFisheye(transform);
|
||||
|
||||
return new THREE.Mesh(geometry, material);
|
||||
}
|
||||
|
||||
private _createDefaultMaterialParameters(
|
||||
private _createMaterialParameters(
|
||||
transform: Transform,
|
||||
texture: THREE.Texture): THREE.ShaderMaterialParameters {
|
||||
texture: THREE.Texture,
|
||||
shader: GLShader): THREE.ShaderMaterialParameters {
|
||||
const scaleX = Math.max(transform.basicHeight, transform.basicWidth) / transform.basicWidth;
|
||||
const scaleY = Math.max(transform.basicWidth, transform.basicHeight) / transform.basicHeight;
|
||||
return {
|
||||
depthWrite: false,
|
||||
fragmentShader: resolveShader(transform.camera, Shader.texture.fragment),
|
||||
fragmentShader: resolveShader(transform.camera, shader.fragment),
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
uniforms: {
|
||||
@ -142,7 +142,7 @@ export class MeshFactory {
|
||||
scale: { value: new Vector2(scaleX, scaleY) },
|
||||
...makeCameraUniforms(transform.camera),
|
||||
},
|
||||
vertexShader: resolveShader(transform.camera, Shader.texture.vertex),
|
||||
vertexShader: resolveShader(transform.camera, shader.vertex),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -2,13 +2,14 @@ import common from "./chunk/common.glsl";
|
||||
import coordinates from "./chunk/coordinates.glsl";
|
||||
|
||||
import bearing_fragment from "./chunk/bearing_fragment.glsl";
|
||||
import color_fragment from "./chunk/color_fragment.glsl";
|
||||
import map_color_fragment from "./chunk/map_color_fragment.glsl";
|
||||
import gl_frag_color_fragment from "./chunk/gl_frag_color_fragment.glsl";
|
||||
import precision_fragment from "./chunk/precision_fragment.glsl";
|
||||
import uniforms_fragment from "./chunk/uniforms_fragment.glsl";
|
||||
import varyings_fragment from "./chunk/varyings_fragment.glsl";
|
||||
|
||||
import extrinsic_vertex from "./chunk/extrinsic_vertex.glsl";
|
||||
import position_vertex from "./chunk/position_vertex.glsl";
|
||||
import gl_position_vertex from "./chunk/gl_position_vertex.glsl";
|
||||
import uniforms_vertex from "./chunk/uniforms_vertex.glsl";
|
||||
import varyings_vertex from "./chunk/varyings_vertex.glsl";
|
||||
|
||||
@ -20,14 +21,15 @@ export const ShaderChunk = {
|
||||
|
||||
// Fragment
|
||||
bearing_fragment,
|
||||
color_fragment,
|
||||
map_color_fragment,
|
||||
gl_frag_color_fragment,
|
||||
precision_fragment,
|
||||
uniforms_fragment,
|
||||
varyings_fragment,
|
||||
|
||||
// Vertex
|
||||
extrinsic_vertex,
|
||||
position_vertex,
|
||||
gl_position_vertex,
|
||||
uniforms_vertex,
|
||||
varyings_vertex,
|
||||
};
|
||||
|
||||
3
src/shader/chunk/gl_frag_color_fragment.glsl.ts
Normal file
3
src/shader/chunk/gl_frag_color_fragment.glsl.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default /* glsl */`
|
||||
gl_FragColor = mapColor;
|
||||
`;
|
||||
@ -3,13 +3,11 @@ export default /* glsl */`
|
||||
float u = uv.x;
|
||||
float v = uv.y;
|
||||
|
||||
vec4 baseColor;
|
||||
vec4 mapColor;
|
||||
if (u >= 0. && u <= 1. && v >= 0. && v <= 1.) {
|
||||
baseColor = texture2D(map, vec2(u, v));
|
||||
baseColor.a = opacity;
|
||||
mapColor = texture2D(map, vec2(u, v));
|
||||
mapColor.a = opacity;
|
||||
} else {
|
||||
baseColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
mapColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
gl_FragColor = baseColor;
|
||||
`;
|
||||
@ -5,7 +5,7 @@ export const vertex = /* glsl */`
|
||||
void main()
|
||||
{
|
||||
#include <extrinsic_vertex>
|
||||
#include <position_vertex>
|
||||
#include <gl_position_vertex>
|
||||
}
|
||||
`;
|
||||
|
||||
@ -23,6 +23,7 @@ void main()
|
||||
{
|
||||
#include <bearing_fragment>
|
||||
#expand <project_to_sfm_invocation>
|
||||
#include <color_fragment>
|
||||
#include <map_color_fragment>
|
||||
#include <gl_frag_color_fragment>
|
||||
}
|
||||
`;
|
||||
|
||||
@ -1,3 +1,14 @@
|
||||
import {
|
||||
Observable,
|
||||
Subject,
|
||||
Subscription,
|
||||
} from "rxjs";
|
||||
import {
|
||||
publishReplay,
|
||||
refCount,
|
||||
startWith,
|
||||
} from "rxjs/operators";
|
||||
|
||||
import {
|
||||
FisheyeCamera,
|
||||
FISHEYE_CAMERA_TYPE,
|
||||
@ -19,7 +30,11 @@ import { GLShader, Shader } from "../shader/Shader";
|
||||
|
||||
export class ProjectionService implements ICameraFactory {
|
||||
private readonly _cameraFactory: { [type: string]: CameraConstructor; } = {};
|
||||
|
||||
private _shader: GLShader;
|
||||
private _shader$: Observable<GLShader>;
|
||||
private _shaderChanged$: Subject<GLShader>;
|
||||
private _shaderSubscription: Subscription;
|
||||
|
||||
constructor() {
|
||||
this.registerCamera(
|
||||
@ -33,6 +48,21 @@ export class ProjectionService implements ICameraFactory {
|
||||
SphericalCamera);
|
||||
|
||||
this._shader = Shader.texture;
|
||||
this._shaderChanged$ = new Subject<GLShader>();
|
||||
this._shader$ = this._shaderChanged$.pipe(
|
||||
startWith(this._shader),
|
||||
publishReplay(1),
|
||||
refCount());
|
||||
|
||||
this._shaderSubscription = this._shader$.subscribe();
|
||||
}
|
||||
|
||||
public get shader$(): Observable<GLShader> {
|
||||
return this._shader$;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._shaderSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
public getShader(): GLShader {
|
||||
@ -57,5 +87,6 @@ export class ProjectionService implements ICameraFactory {
|
||||
}
|
||||
|
||||
this._shader = shader;
|
||||
this._shaderChanged$.next(this._shader);
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +50,7 @@ import { ViewerReferenceEvent } from "./events/ViewerReferenceEvent";
|
||||
import { IDataProvider } from "../external/api";
|
||||
import { ViewerResetEvent } from "./events/ViewerResetEvent";
|
||||
import { CameraConstructor } from "../geometry/interfaces/ICamera";
|
||||
import { GLShader } from "../shader/Shader";
|
||||
|
||||
/**
|
||||
* @class Viewer
|
||||
@ -1649,6 +1650,10 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
this._container.renderService.renderMode$.next(renderMode);
|
||||
}
|
||||
|
||||
public setShader(shader: GLShader): void {
|
||||
this._navigator.projectionService.setShader(shader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the viewer's transition mode.
|
||||
*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user