Skip to content

Commit

Permalink
Update doc (till LayerRTI)
Browse files Browse the repository at this point in the history
  • Loading branch information
thera2002 committed Nov 1, 2024
1 parent 0fdcd22 commit f300694
Show file tree
Hide file tree
Showing 9 changed files with 1,006 additions and 184 deletions.
76 changes: 74 additions & 2 deletions src/LayerAnnotationImage.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,58 @@ import { Shader } from './Shader.js'
import { LayoutTileImages } from './LayoutTileImages.js';

/**
* Class which extend LayerAnnotation. Support Image Annotations.
* Each annotation corresponds to a tile (currently single resolution)
* @typedef {Object} LayerAnnotationImageOptions
* @property {string} [url] - URL to the annotations JSON file
* @property {string} [path] - Base path for annotation image files
* @property {string} [format='vec4'] - Raster format for image data
* @extends LayerAnnotationOptions
*/

/**
* LayerAnnotationImage extends LayerAnnotation to provide support for image-based annotations.
* Each annotation corresponds to a single tile in the layer, with customizable visibility
* and shader-based rendering.
*
* Features:
* - Image-based annotation rendering
* - Per-annotation visibility control
* - Custom shader support for image processing
* - Automatic texture management
* - WebGL/WebGL2 compatibility
* - Multi-format raster support
*
* The class handles:
* - Image loading and caching
* - Texture creation and binding
* - Shader setup and compilation
* - Tile visibility management
* - WebGL state management
*
* @extends LayerAnnotation
*
* @example
* ```javascript
* // Create image annotation layer
* const imageAnnoLayer = new OpenLIME.LayerAnnotationImage({
* url: 'annotations.json',
* path: './annotation-images/',
* format: 'vec4'
* });
*
* // Configure visibility
* imageAnnoLayer.setAllTilesVisible(true);
* imageAnnoLayer.setTileVisible(0, false); // Hide first annotation
*
* // Add to viewer
* viewer.addLayer('imageAnnotations', imageAnnoLayer);
* ```
*/
class LayerAnnotationImage extends LayerAnnotation {
/**
* Creates a new LayerAnnotationImage instance
* @param {LayerAnnotationImageOptions} options - Configuration options
* @throws {Error} If path is not specified (warns in console)
*/
constructor(options) {
const url = options.url;
if (options.path == null) {
Expand Down Expand Up @@ -38,16 +85,29 @@ class LayerAnnotationImage extends LayerAnnotation {
this.addEvent('loaded', initCallback);
}

/**
* Gets the number of annotations in the layer
* @returns {number} Number of annotations
*/
length() {
return this.annotations.length;
}

/**
* Sets visibility for a specific annotation/tile
* @param {number} index - Index of the annotation
* @param {boolean} visible - Whether the annotation should be visible
*/
setTileVisible(index, visible) {
this.layout.setTileVisible(index, visible);
//this.annotations[index].needsUpdate = true;
//this.emit('update');
}

/**
* Sets visibility for all annotations/tiles
* @param {boolean} visible - Whether all annotations should be visible
*/
setAllTilesVisible(visible) {
this.layout.setAllTilesVisible(visible);
// for(let a of this.annotations) {
Expand All @@ -56,6 +116,13 @@ class LayerAnnotationImage extends LayerAnnotation {
//this.emit('update');
}

/**
* Renders a specific tile/annotation
* @param {Object} tile - Tile object containing texture information
* @param {number} index - Index of the tile
* @throws {Error} If tile is missing textures
* @private
*/
drawTile(tile, index) {
if (tile.missing != 0)
throw "Attempt to draw tile still missing textures"
Expand All @@ -75,6 +142,11 @@ class LayerAnnotationImage extends LayerAnnotation {
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, byteOffset);
}

/**
* Configures the shader for rendering annotations
* @param {string} rasterFormat - Format of the raster data ('vec4', etc)
* @private
*/
setupShader(rasterFormat) {
let samplers = [];
let N = this.rasters.length;
Expand Down
127 changes: 111 additions & 16 deletions src/LayerBRDF.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,80 @@
import { Layer } from './Layer.js'
import { Layer } from './Layer.js'
import { Raster } from './Raster.js'
import { ShaderBRDF } from './ShaderBRDF.js'

/**
* Extends {@link Layer}.
* @param {options} options Same as {@link Layer}, but channels(ks,kd,normals,gloss) are required.
* @typedef {Object} LayerBRDFOptions
* @property {Object} channels - Required channels for BRDF rendering
* @property {string} channels.kd - URL to diffuse color map (required)
* @property {string} channels.ks - URL to specular color map (optional)
* @property {string} channels.normals - URL to normal map (required)
* @property {string} channels.gloss - URL to glossiness/roughness map (optional)
* @property {Object} [colorspaces] - Color space definitions for material properties
* @property {('linear'|'srgb')} [colorspaces.kd='linear'] - Color space for diffuse map
* @property {('linear'|'srgb')} [colorspaces.ks='linear'] - Color space for specular map
* @property {number} [brightness=1.0] - Overall brightness adjustment
* @property {number} [gamma=2.2] - Gamma correction value
* @property {number[]} [alphaLimits=[0.01, 0.5]] - Range for glossiness/roughness
* @property {number[]} [monochromeMaterial=[0.80, 0.79, 0.75]] - RGB color for monochrome rendering
* @property {number} [kAmbient=0.1] - Ambient light coefficient
* @extends LayerOptions
*/

/**
* LayerBRDF implements real-time BRDF (Bidirectional Reflectance Distribution Function) rendering.
*
* The BRDF model describes how light reflects off a surface, taking into account:
* - Diffuse reflection (rough, matte surfaces)
* - Specular reflection (mirror-like reflections)
* - Surface normals (microscopic surface orientation)
* - Glossiness/roughness (surface micro-structure)
*
* Features:
* - Real-time light direction control
* - Multiple material channels support
* - Customizable material properties
* - Interactive lighting model
* - Gamma correction
* - Ambient light component
*
* Technical implementation:
* - Uses normal mapping for surface detail
* - Supports both linear and sRGB color spaces
* - Implements spherical light projection
* - Handles multi-channel textures
* - GPU-accelerated rendering
*
* @extends Layer
*
* @example
* ```javascript
* // Create BRDF layer with all channels
* const brdfLayer = new OpenLIME.LayerBRDF({
* channels: {
* kd: 'diffuse.jpg',
* ks: 'specular.jpg',
* normals: 'normals.jpg',
* gloss: 'gloss.jpg'
* },
* colorspaces: {
* kd: 'srgb',
* ks: 'linear'
* },
* brightness: 1.2,
* gamma: 2.2
* });
*
* // Update light direction
* brdfLayer.setLight([0.5, 0.5], 500, 'ease-out');
* ```
*/
class LayerBRDF extends Layer {
/**
* Creates a new LayerBRDF instance
* @param {LayerBRDFOptions} options - Configuration options
* @throws {Error} If required channels (kd, normals) are not provided
* @throws {Error} If rasters option is not empty
*/
constructor(options) {
options = Object.assign({
brightness: 1.0,
Expand All @@ -18,16 +85,16 @@ class LayerBRDF extends Layer {
}, options);
super(options);

if(Object.keys(this.rasters).length != 0)
if (Object.keys(this.rasters).length != 0)
throw "Rasters options should be empty!";

if(!this.channels)
if (!this.channels)
throw "channels option is required";

if(!this.channels.kd || !this.channels.normals)
if (!this.channels.kd || !this.channels.normals)
throw "kd and normals channels are required";
if(!this.colorspaces) {

if (!this.colorspaces) {
console.log("LayerBRDF: missing colorspaces: force both to linear");
this.colorspaces['kd'] = 'linear';
this.colorspaces['ks'] = 'linear';
Expand All @@ -51,7 +118,7 @@ class LayerBRDF extends Layer {

this.layout.setUrls(urls);
this.addControl('light', [0, 0]); // This is a projection to the z=0 plane.

let shader = new ShaderBRDF({
'label': 'Rgb',
'samplers': samplers,
Expand All @@ -67,6 +134,13 @@ class LayerBRDF extends Layer {
this.setShader('brdf');
}

/**
* Projects a 2D point onto a sphere surface
* Used for converting 2D mouse/touch input to 3D light direction
* @param {number[]} p - 2D point [x, y] in range [-1, 1]
* @returns {number[]} 3D normalized vector [x, y, z] on sphere surface
* @static
*/
static projectToSphere(p) {
let px = p[0];
let py = p[1];
Expand All @@ -82,11 +156,15 @@ class LayerBRDF extends Layer {
return [px, py, z];
}

// Idea from SGI trackball, siggraph 1988 (e.g., http://gxsm.sourceforge.net/gxsmsrcdoxy/html/d2/db2/gxsm_2trackball_8C-source.html)
// The point is projected to a sphere in the center, and deformed to a
// hyperbolic sheet of rotation away from it in order to avoid
// the acceleration due to projection on an increasingly vertical
// surface
/**
* Projects a 2D point onto a flattened sphere using SGI trackball algorithm.
* This provides more intuitive light control by avoiding acceleration near edges.
* Based on SIGGRAPH 1988 paper on SGI trackball implementation.
*
* @param {number[]} p - 2D point [x, y] in range [-1, 1]
* @returns {number[]} 3D normalized vector [x, y, z] on flattened sphere
* @static
*/
static projectToFlattenedSphere(p) {
const R = 0.8; const R2 = R * R;
const RR = R * Math.SQRT1_2; const RR2 = RR * RR;
Expand All @@ -106,20 +184,37 @@ class LayerBRDF extends Layer {
return [px / r, py / r, z / r];
}

setLight(light, dt, easing='linear') {
/**
* Sets the light direction with optional animation
* @param {number[]} light - 2D vector [x, y] representing light direction
* @param {number} [dt] - Animation duration in milliseconds
* @param {string} [easing='linear'] - Animation easing function
*/
setLight(light, dt, easing = 'linear') {
this.setControl('light', light, dt, easing);
}

/**
* Updates light control interpolation and shader uniforms
* @returns {boolean} Whether all interpolations are complete
* @override
* @private
*/
interpolateControls() { // FIXME Wrong normalization
let done = super.interpolateControls();
// let light = LayerBRDF.projectToSphere(this.controls['light'].current.value);
// let light = LayerBRDF.projectToSphere(this.controls['light'].current.value);
let light = LayerBRDF.projectToFlattenedSphere(this.controls['light'].current.value);
this.shader.setLight([light[0], light[1], light[2], 0]);
return done;
}
}


/**
* Register this layer type with the Layer factory
* @type {Function}
* @private
*/
Layer.prototype.types['brdf'] = (options) => { return new LayerBRDF(options); }

export { LayerBRDF }
Loading

0 comments on commit f300694

Please sign in to comment.