Rendering component

This component defines the basic properties of geometric objects, and nodes to render triangles, lines and points. See Geometry3D component for more comfortable geometric objects like polygons and spheres.

See also X3D specification of the Rendering component.

Contents:

1. Indexed or non-indexed?

Most of the nodes in this section have both indexed (like TIndexedTriangleSetNode) and non-indexed (like TTriangleSetNode) versions.

The "indexed" versions allow to refer to the same vertex (on the Coord list) multiple times, since you specify the vertex once in TCoordinateNode, and then can use the same index multiple times e.g. in the array you pass to TIndexedTriangleSetNode.SetIndex.

If you have a large mesh that has vertexes shared by neighboring polygons, it usually makes sense to use "indexed" versions. They will be slightly more optimal — the vertex is specified once, some calculations can run on it just once (even if it is then used by multiple polygons). This is often a natural representation of a triangle set when generated by 3D authoring tools like Blender.

Otherwise, for smaller meshes, and meshes that would not reuse the vertexes anyway (because they specify mostly unconnected triangles), you can use non-indexed versions.

Most of geometries created "by code" usually qualify as "smaller meshes" :) So just use whatever is most natural, which is usually non-indexed versions. It probably doesn't make sense to even think about optimizing when vertex count < 100.

2. Supported nodes

  • Coordinate(Pascal API: TCoordinateNode),
    Color(Pascal API: TColorNode),
    ColorRGBA(Pascal API: TColorRGBANode),
    Normal(Pascal API: TNormalNode)

    These nodes provide information about positions/colors/normal vectors to various geometry nodes. They are used e.g. by various nodes in this component (like IndexedTriangleSet(Pascal API: TIndexedTriangleSetNode)), and in Geometry3D component (like IndexedFaceSet(Pascal API: TIndexedFaceSetNode)).

  • Triangles:

    IndexedTriangleSet(Pascal API: TIndexedTriangleSetNode),
    TriangleSet(Pascal API: TTriangleSetNode),

    IndexedTriangleFanSet(Pascal API: TIndexedTriangleFanSetNode),
    TriangleFanSet(Pascal API: TTriangleFanSetNode),
    IndexedTriangleStripSet(Pascal API: TIndexedTriangleStripSetNode),
    TriangleStripSet(Pascal API: TTriangleStripSetNode)

    Notes:

    • If you're looking for quads, use instead IndexedQuadSet, QuadSet from CAD geometry component.

    • If you're looking for polygons (with arbitrary number of vertexes for each face), use instead IndexedFaceSet from Geometry3D component.

    • The geometry nodes have many fields to specify positions, and various per-vertex information (normal vectors, colors, attrib with per-vertex attributes for shaders, fogCoord with per-vertex fog depth).

    • The TriangleFanSet and TriangleStripSet nodes have a special limitation: if you will use colors (colors are always per-vertex on these primitives, according to X3D spec) and request generation of per-face normals at the same time, and the shape is lit (has material), then shading results will be slightly incorrect. Like this:

      #X3D V3.0 utf8
      PROFILE Interchange
      
      Shape {
        appearance Appearance { material Material { } }
        geometry TriangleFanSet {
          coord Coordinate { point [ 0 0 0, 1 0 0, 1 1 0, 0.5 1.5 0.5 ] }
          fanCount 4
          color Color { color [ 1 0 0, 0 1 0, 0 0 1, 1 1 1 ] }
          normalPerVertex FALSE
        }
      }
      

      Unfortunately, this is unfixable without falling back to worse rendering methods (instead of using triangle fan/strip on GPU). Shading has to be smooth to interpolate per-vertex colors, and at the same time the same vertex may require different normals on different faces. One day we will implement a special fallback for this case (to internally convert fans and strips to IndexedTriangleSet in such special case).

    • X3D specification doesn't specify what to do for triangle/quad sets when appearance specify a texture but no texCoord is given. Our engine currently takes the IndexedFaceSet approach for automatic generation of texture coords in this case.

  • Lines:

    LineSet(Pascal API: TLineSetNode),
    IndexedLineSet(Pascal API: TIndexedLineSetNode)

  • Points:

    PointSet(Pascal API: TPointSetNode)

    Using PointSet is the fastest and easiest way to render points. See an example X3D file. You can write the points in an X3D file or define the nodes using Pascal code, as always, see an example how to build X3D graph in Pascal.

    To control the size of the point, set Scene.RenderOptions.PointSize.

    This uses OpenGL(ES) rendering of points. So:

    • It's really fast (milions of points are not a problem, when placed in one PointSet).

    • There are GPU-specific (OpenGL-implementation specific, actually) limits on the possible point sizes. For desktop OpenGL, small sizes like 1-8 are usually supported, but (strictly speaking) only size 1 is absolutely guaranteed to be supported (see GL_POINT_SIZE_RANGE). For mobile OpenGLES, controlling the point size is not possible (so it's always like 1).

    • Using anti-aliasing affects how the large points look (as a rect or circle). See glPointSize docs.

    This is the fastest method to render points (fastest to use, and fastest to render) but you need to watch for the limitations mentioned above.

    Note: To display a number of 2D points, you can also use DrawPrimitive2D procedure with Mode = pmPoints. It has a parameter where you specify PointSize. Note that this also uses native OpenGL(ES) points, so controlling the point size is limited, just like noted above.

  • ClipPlane(Pascal API: TClipPlaneNode)

    This node allows to clip the geometry by an arbitrary plane in 3D space. In effect, the geometry is "cut" in the middle, with only one part visible.

    Notes:

    • There is a limit on the number of clipping planes that may be enabled at the same time on a single shape. This limit is at least six (see view3dscene "Help -> OpenGL Information", look at "Max clip planes" line, to know the limit of your GPU). Following X3D spec, we treat the "more global" clipping planes as more important.

    • Clipping planes are a purely visual effect. The clipped (invisible) geometry is still taken into account for collision detection, mouse picking, bounding volume calculation etc.

    • Clipping planes don't affect background nodes (X3DBackgroundNode).

3. Example of creating a PointSet in Pascal

This is an example how to construct in Pascal a scene with points in a PointSet and render them or save to file:

{ Show points using TPointSetNode. }
 
uses CastleVectors, CastleWindow, X3DNodes, X3DLoad, CastleScene, CastleViewport,
  CastleCameras;
 
var
  PointSet: TPointSetNode;
  PointCoordinates: TCoordinateNode;
  PointShape: TShapeNode;
  Root: TX3DRootNode;
  Window: TCastleWindow;
  Scene: TCastleScene;
  Viewport: TCastleViewport;
begin
  Window := TCastleWindow.Create(Application);
  Window.Open;
 
  Viewport := TCastleViewport.Create(Application);
  Viewport.FullSize := true;
  Viewport.AutoCamera := true;
  Window.Controls.InsertFront(Viewport);
 
  Viewport.InsertFront(TCastleExamineNavigation.Create(Application));
 
  PointCoordinates := TCoordinateNode.Create;
  PointCoordinates.SetPoint(
    [Vector3(0, 0, 0),
     Vector3(0, 1, 0),
     Vector3(1, 1, 0),
     Vector3(1, 0, 0),
     Vector3(0.5, 0.5, 0)
   ]);
 
  PointSet := TPointSetNode.Create;
  PointSet.Coord := PointCoordinates;
 
  PointShape := TShapeNode.Create;
  PointShape.Geometry := PointSet;
 
  Root := TX3DRootNode.Create;
  Root.AddChildren(PointShape);
 
  { You can now save Root to X3D file,
    or render Root (loading it to Scene, adding Scene to TCastleViewport).
    Or both, as the example below shows.
 
    Notes about memory management:
 
    - the PointCoordinates are owned by PointSet,
    - PointSet is owned by PointShape,
    - PointShape is owned by Root,
    - Root is owned by Scene (because we call Scene.Load with 2nd parameter "true"),
    - and Scene is owned by Application.
 
    So in this example, everything will be automatically freed when
    Application is freed (which happens in CastleWindow unit finalization).
    If you would not call "Scene.Load(Root, true)", then you should free Root
    manually at the end, by "FreeAndNil(Root)".
  }
 
  SaveNode(Root, 'my_points.x3d');
 
  Scene := TCastleScene.Create(Application);
  Scene.Load(Root, true);
  Scene.PreciseCollisions := true;
  Scene.RenderOptions.PointSize := 10;
 
  Viewport.Items.Add(Scene);
 
  Application.Run;
end.

4. Example of creating a TriangleSet in Pascal

This is an example how to construct in Pascal a scene with TriangleSet:

{ Build shape from triangles using TTriangleSetNode. }
 
uses CastleWindow, CastleViewport, CastleScene, X3DNodes, CastleFilesUtils,
  CastleColors, CastleVectors, CastleTimeUtils, CastleSceneCore;
 
var
  Scene: TCastleScene;
 
function BuildScene: TX3DRootNode;
var
  Shape: TShapeNode;
  Geometry: TTriangleSetNode;
  Material: TPhysicalMaterialNode;
  Coord: TCoordinateNode;
begin
  Coord := TCoordinateNode.Create;
  // for TTriangleSetNode, each 3 points define a triangle
  Coord.SetPoint([
    Vector3(-1, -1, 0),
    Vector3( 1, -1, 0),
    Vector3( 1,  1, 0),
 
    Vector3(-1, 2, 0),
    Vector3( 1, 2, 0),
    Vector3( 1, 4, 0)
  ]);
 
  Geometry := TTriangleSetNode.CreateWithShape(Shape);
  Geometry.Solid := false; // see them from both sides
  Geometry.Coord := Coord;
 
  Material := TPhysicalMaterialNode.Create;
  Material.BaseColor := YellowRGB;
 
  Shape.Appearance := TAppearanceNode.Create;
  Shape.Appearance.Material := Material;
 
  Result := TX3DRootNode.Create;
  Result.AddChildren(Shape);
end;
 
var
  Window: TCastleWindow;
  Viewport: TCastleViewport;
  Headlight: TCastleDirectionalLight;
begin
  Window := TCastleWindow.Create(Application);
  Window.Open;
 
  Headlight := TCastleDirectionalLight.Create(Application);
  Headlight.Intensity := 10;
 
  Viewport := TCastleViewport.Create(Application);
  Viewport.FullSize := true;
  Viewport.Camera.SetView(
    Vector3(0, 0, 10), // position
    Vector3(0, 0, -1), // look direction
    Vector3(0, 1, 0) // up
  );
  // add a headlight
  Viewport.Camera.Add(Headlight);
  Window.Controls.InsertFront(Viewport);
 
  Scene := TCastleScene.Create(Application);
  Scene.Load(BuildScene, true);
 
  Viewport.Items.Add(Scene);
 
  Application.Run;
end.

5. Example of creating a TriangleFanSet in Pascal

This is an example how to construct in Pascal a scene with TriangleFanSet:

{ Build shape from triangles using TTriangleFanSetNode. }
 
uses CastleWindow, CastleViewport, CastleScene, X3DNodes, CastleFilesUtils,
  CastleColors, CastleVectors, CastleTimeUtils, CastleSceneCore;
 
var
  Scene: TCastleScene;
 
function BuildScene: TX3DRootNode;
var
  Shape: TShapeNode;
  Geometry: TTriangleFanSetNode;
  Material: TPhysicalMaterialNode;
  Coord: TCoordinateNode;
begin
  Coord := TCoordinateNode.Create;
  Coord.SetPoint([
    // 1st fan
    Vector3(-1, -1, 0),
    Vector3( 1, -1, 0),
    Vector3( 1,  1, 0), // 1st triangle of 1st fan ends here
 
    Vector3(0.6, 1, 0), // 2nd triangle of 1st fan ends here
    Vector3(0.2, 1, 0), // 3rd triangle of 1st fan ends here
    Vector3(-0.2, 1, 0), // 4th triangle of 1st fan ends here
 
    // 2nd fan
    Vector3(-1, 2, 0),
    Vector3( 1, 2, 0),
    Vector3( 1, 4, 0), // 1st triangle of 2nd fan ends here
 
    Vector3(0.6, 4, 0), // 2nd triangle of 2nd fan ends here
    Vector3(0.2, 4, 0), // 3rd triangle of 2nd fan ends here
    Vector3(-0.2, 4, 0) // 4th triangle of 2nd fan ends here
  ]);
 
  Geometry := TTriangleFanSetNode.CreateWithShape(Shape);
  Geometry.Solid := false; // see them from both sides
  Geometry.Coord := Coord;
  // define 2 fans
  Geometry.SetFanCount([6, 6]);
 
  Material := TPhysicalMaterialNode.Create;
  Material.BaseColor := YellowRGB;
 
  Shape.Appearance := TAppearanceNode.Create;
  Shape.Appearance.Material := Material;
 
  Result := TX3DRootNode.Create;
  Result.AddChildren(Shape);
end;
 
var
  Window: TCastleWindow;
  Viewport: TCastleViewport;
  Headlight: TCastleDirectionalLight;
begin
  Window := TCastleWindow.Create(Application);
  Window.Open;
 
  Headlight := TCastleDirectionalLight.Create(Application);
  Headlight.Intensity := 10;
 
  Viewport := TCastleViewport.Create(Application);
  Viewport.FullSize := true;
  Viewport.Camera.SetView(
    Vector3(0, 0, 10), // position
    Vector3(0, 0, -1), // look direction
    Vector3(0, 1, 0) // up
  );
  // add a headlight
  Viewport.Camera.Add(Headlight);
  Window.Controls.InsertFront(Viewport);
 
  Scene := TCastleScene.Create(Application);
  Scene.Load(BuildScene, true);
  // makes it easier to see triangle
  Scene.RenderOptions.WireframeEffect := weSolidWireframe;
 
  Viewport.Items.Add(Scene);
 
  Application.Run;
end.