Terrain height/slope visualization via autogenerated texture coordinates
Contents
Summary of discussion on geospatial email list in 7/2015
Looking for a way to color contour elevation or slope of terrain (or other surfaces), new (GEO-)HEIGHT and (GEO-)SLOPE modes for the TextureCoordinateGenerator were proposed and developed. With this modes, it is only necessary to provide a colormap as a 1d texture in the Appearance node and the TextureCoordinateGenerator node with one of the new modes with a geometry node such (Geo)ElevationGrid in order to achieve the desired effect. An implementation of the new modes is available in a github fork of x3dom.
motivation and initial discussion
It is very common to contour terrain DEMs or to color by them elevation. A recommended solution on the x3dom-user mailing list was to define texture coordinates as [normalized height, 0] for each grid point and then use a 1d texture to map colors to height. This is a good solution since it allows for easily swapping out the color scale. For example, it is possible to limit the number of colors for a striped, contoured appearance.
It is very possible to use this solution for a (Geo)Elevationgrid: calculate the texture coordinates from the height field and put them in a texture coordinate node. However, since it is such a common situation and since there is a (minor) calculation involved, it would make sense to make this procedure much more accessible in some way. At first, introducing a (convenience) boolean option "heightTexCoords" to GeoElevationgrid which automatically generates the texture coordinates based on height was considered.
By default the option would determine min. and max. of the height field and generate texture coordinates (height-min.)/(max.-min.) , 0 for each node.
It would then be also very useful to able to specify min. and max. height directly by a MFDouble field "height_range" [min., max.] .
Another option may be discretization of the texture coordinates into a limited number of values by a SFInt32 field "height_steps": U_discrete = int(U * hsteps)/hsteps or so where U is the normalized height. This effect can be achieved with higher fidelity by a discretized texture map as well but would be convenient to have. For example, it would be straightforward to generate 100m contours by providing a height_range of [-3000,0] and height_steps of 30. Some more discussion and thought would be required since the contours would not be very accurate due to information loss. It would be probably better to have such a discretization option for the texture map.
If multiple options are provided, the source for texture coordinates would need to be prioritized:
1) explicitly defined: TextureCoordinate node 2) heightTexCoords 3) default as in spec.
Testing for correctness should be possible by comparing manually added texture coordinates with automatically generated texture coordinates.
Other considerations from an application perspective are:
1. It would be nice if this technique could be applied to mesh data as well as grid data;
2. Marine geologists and geophysicists like to shade the color according to slope magnitude or synthetic illumination (see the man page for mbm_grdtiff). With X3D we can add a light to the scene and get synthetic illumination, but shading by slope magnitude would have to be done some other way. The mbm_grdtiff tool has several options that serve to distill decades of use case capture – maybe some of these are optional attributes that could go along with a new heightTexCoords attribute.
3. If a color node is used there should be a way to pass in a color lookup table. (An advantage of using the more general texture approach is that the x3d author specifies the color lookup with the texture.)
Finding a general solution for mesh data including IndexedFaceSets leads to the suggestion to define a new mode for TextureCoordinateGenerator. Then it would be necessary to distinguish between geocentric coordinates and regular coordinates to determine height, making the problem a little complex.
Slope or dip based coloring/texturing could be quite interesting and is something we actually do quite a bit in geosciences. It would be possible to calculate slope (and aspect) for each grid node, for example according to
http://resources.arcgis.com/en/help/main/10.1/index.html#//009z000000vz000000
using the 3x3 grid around a node. At the borders, one could just use the closest values to fill in. It should also be possible to use the calculated mesh normals.
Another approach would be using normals which are calculated or provided for mesh vertices. Since geocentric positions are used, it would be necessary to use a reference vertical direction.
The advantage of using colors would be that a color node may be a bit more user friendly than textures. However, X3D only has rgb colors, not palleted colors, and pixel textures effectively allow for using color values similar to color nodes.
planning discussion on the TextureCoordinateGenerator node
The TextureCoordinateGenerator mode appears to be designed to enable dynamic updates of texture coordinates based on eye position/orientation, vertex positions and vertex normals using a number of modes which correspond to fixed functions of these inputs. As an OpenGL function this concept is replaced by fully programmable shaders. For example, x3dom implements the SPHERE mode in code inserted into the full shader used for rendering. Other modes, such as SPHERE-LOCAL are implemented outside the shader because they are static with respect to the eye position/orientation.
A SLOPE mode would be somewhat similar SPHERE-LOCAL mode in that it would use components of the local normal, here the z component for S and 0 for T. This should would work for non-geospatial meshes where the XZ plane is horizontal. Similarly, it should be possible to add a HEIGHT mode which uses the normalized Y coordinate for S.
One presumably could do this as part of the shader code as well although in x3dom it is harder to see where to start. Perhaps follow how the SPHERE mode is coded ?
There is a "COORD" mode in the spec.
which uses vertex coordinates in some way and could be similar to a HEIGHT mode. It does not seem to be implemented in x3dom.
For geospatial meshes it makes sense to define a specialized mode, a GEO-HEIGHT mode, to indicate that the coordinate system is geocentric. It would be first necessary to derive the heights from the geocentric mesh coordinates (potentially taking into account GeoOrigin transformations). The other option would be to look into the parent node, and use the height field in case of GeoElevationGrid, or the z-component of GeoCoordinates but this would not work for geocentric geosystem.
By default, S would be scaled to full height range of the mesh. One could use the parameter field of the TextureCoordinateGenerator node to specify a custom height range and perhaps a discretization as outlined earlier.
For a GEO-SLOPE mode, it would be necessary to calculate the angle between the provided normal and the local Up (or Down) direction. For rotateYUp scenes presumably one could just use the normal, or undo the rotation.
That brings up the question if it is acceptable to introduce geo-modes to TextureCoordinateGenerator in terms of x3d components and profiles. It may be necessary to introduce a GeoTextureCoordinateGenerator node ?
In summary, leaning towards using new TextureCoordinateGenerator modes instead of new GeoElevationGrid fields would cover more use cases. However, it requires some more processing to deal with the geocentric coordinates, eg. may be a bit harder to implement. In x3dom, it would touch Mesh.js which is used by a lot of nodes.
first concrete examples
...
new modes proposal
...
shaded relief effects
...