Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
billboard [2019/03/12 13:27] – [Using a grass billboard] adminbillboard [2020/04/11 14:30] – [Billboard] admin
Line 1: Line 1:
 ====== Billboard ====== ====== Billboard ======
  
-A billboard controls proxy objects of a basis mesh instance (source object).+A billboard controls proxy instances of a basis mesh (source object).
  
 The billboard component is a mesh itself, into which proxies are merged. Much faster rendering is possible if proxy meshes are merge into one mesh, instead of rendering each mesh. The billboard component is a mesh itself, into which proxies are merged. Much faster rendering is possible if proxy meshes are merge into one mesh, instead of rendering each mesh.
 +
 +Restriction: multi-mesh models will be used as multiple source objects and not merged as one object. For example: if you have a model including a cube and sphere mesh, the cube and sphere will be registered as source objects. They will not be handled as single mesh.
  
 We typically use this technique for grass or trees. We typically use this technique for grass or trees.
 It is not suitable for animated meshes. It is not suitable for animated meshes.
  
-===== Using a grass billboard =====+===== Example: Grass Billboard =====
  
 In this example we will display grass on multiplied planes inside of the billboard component. In this example we will display grass on multiplied planes inside of the billboard component.
Line 35: Line 37:
   begin   begin
     LPoolE := Bitmaps.Add() as TGorillaBitmapPoolEntry;     LPoolE := Bitmaps.Add() as TGorillaBitmapPoolEntry;
 +    LPoolE.DisplayName := 'Grass1';
     LPoolE.Bitmap.LoadFromFile('grass1.png');     LPoolE.Bitmap.LoadFromFile('grass1.png');
  
     LPoolE := Bitmaps.Add() as TGorillaBitmapPoolEntry;     LPoolE := Bitmaps.Add() as TGorillaBitmapPoolEntry;
 +    LPoolE.DisplayName := 'Grass2';
     LPoolE.Bitmap.LoadFromFile('grass2.png');     LPoolE.Bitmap.LoadFromFile('grass2.png');
  
     LPoolE := Bitmaps.Add() as TGorillaBitmapPoolEntry;     LPoolE := Bitmaps.Add() as TGorillaBitmapPoolEntry;
 +    LPoolE.DisplayName := 'Grass3';
     LPoolE.Bitmap.LoadFromFile('grass3.png');     LPoolE.Bitmap.LoadFromFile('grass3.png');
  
     LPoolE := Bitmaps.Add() as TGorillaBitmapPoolEntry;     LPoolE := Bitmaps.Add() as TGorillaBitmapPoolEntry;
 +    LPoolE.DisplayName := 'Grass4';
     LPoolE.Bitmap.LoadFromFile('grass4.png');     LPoolE.Bitmap.LoadFromFile('grass4.png');
   end;     end;  
Line 50: Line 56:
   FGrass := TGorillaBillboard.Create(FGorilla);   FGrass := TGorillaBillboard.Create(FGorilla);
   FGrass .Parent := FGorilla;   FGrass .Parent := FGorilla;
-  FGrass.SourceObject := LBillboard;+  FGrass.AddSourceObject(LBillboard);
   FGrass.MaterialSource := LGrassMat;   FGrass.MaterialSource := LGrassMat;
   FGrass.SetSize(GORILLA_BILLBOARD_SIZE, GORILLA_BILLBOARD_SIZE, GORILLA_BILLBOARD_SIZE);   FGrass.SetSize(GORILLA_BILLBOARD_SIZE, GORILLA_BILLBOARD_SIZE, GORILLA_BILLBOARD_SIZE);
  
   // create an individual filling algorithm to multiply the grass planes   // create an individual filling algorithm to multiply the grass planes
-  LAlg := TGorillaBillboardRectFilling.Create(FGrass);+  // on elder Gorilla3D version TGorillaBillboardRectFilling 
 +  LAlg := TGorillaBillboardFlatFilling.Create(FGrass);
   try   try
 +    LAlg.Count := 1000;
     FGrass.Fill(LAlg, true);     FGrass.Fill(LAlg, true);
   finally   finally
Line 62: Line 70:
   end;   end;
 </file> </file>
 +
 +==== Interact with grass shader ====
 +
 +The default grass shader provides functionality to bend the grass planes at a certain point.
 +To enable interaction, f.e. at mouse position, we can simply configure the TGorillaGrassMaterialSource:
 +
 +<file pascal Form1.pas>
 +  FGrassMat.SpotRadius := 0.5;
 +  FGrassMat.SpotEnabled := true;
 +</file>
 +
 +And in the OnMouseMove event we can set the current interaction point like this:
 +
 +<file pascal Form1.pas>
 +procedure TForm1.DoOnViewportMouseMove(ASender : TObject; AShiftState : TShiftState;
 +  X, Y : Single);
 +var LPt3D : TPoint3D;
 +    LRayPos, LRayDir : TVector3D;
 +begin
 +  if FMove then
 +  begin
 +    if (ssLeft in AShiftState) then
 +    begin
 +      FGorilla.BeginUpdate();
 +      try
 +        LPt3D := FGorilla.ScreenToWorld(PointF(X, Y));
 +        FGorilla.Context.Pick(X, Y, TProjection.Camera, LRayPos, LRayDir);
 +        FGrass.RayCastIntersect(LRayPos, LRayDir, LPt3D);
 +
 +        // apply scaled light position
 +        FGrassMat.Spot := TPoint3D(LPt3D);
 +      finally
 +        FGorilla.EndUpdate();
 +      end;
 +    end;
 +
 +    FLatest := PointF(X, Y);
 +  end;
 +end;
 +</file>
 +
 +===== Example: Terrain Grass Billboard =====
 +
 +Since v0.8.1+ we've implemented a lot of new backend features (bounding volume hierarchy for TMeshDef, multiple billboard source objects, TGorillaModel support), which allows to multiple meshes and place those onto a terrain surface.
 +
 +The following example will create 64 billboards, filled up with 64 copies of a grass template model. The billboards will be attached as childs to the terrain and rendered above.
 +All billboards will be placed as grid above the complete terrain.
 +
 +__Notice:__ We could multiply the template model into a single billboard component, but the idea behind it, is to use the performance optimization of frustum culling.
 +By frustum culling only the visible billboard chunks will be rendered, therefore we can reduce vertex rendering a lot.
 +
 +So at first we will load a grass template model. //Keep the mesh as simple as possible, because vertex count will increase fastly.//
 +
 +To place a grass model copy in correct y-position on the terrain, we need to know vertices of the terrain. The new feature of bounding volume hierarchy (BVH) computation for TMeshDef's helps here a lot. It is 1000 times faster, than iterating of all vertices to find the correct position.
 +
 +For template model multiplication we use the new TGorillaBillboardTerrainFilling class, which automatically reads y-position from BVH.
 +
 +<file pascal>
 +const
 +  MAP_SIZE = 900; // terrain size (quadratic)
 +  MAP_HEIGHT = MAP_SIZE / 4;
 +  
 +var FGrassTemps : Array[0..0] of TGorillaModel;
 +
 +procedure TForm1.CreateGrass(ATerrain : TGorillaTerrain; const AAssetsPath : String);
 +const  GORILLA_BILLBOARD_SIZE = 1;
 +      GORILLA_MODEL_GRASS_1 = 'lowpoly-grass-planes.obj';
 +
 +var LTexPath      : String;
 +    LGrass        : TGorillaBillboard;
 +    LFillAlg      : TGorillaBillboardTerrainFilling;
 +    LChunks       : Integer;
 +    LChunksPerRow : Integer;
 +    i             : Integer;
 +    LChunkSize    : TPoint3D;
 +    LX, LZ        : Single;
 +begin
 +  if not Assigned(ATerrain) then
 +    raise Exception.Create('terrain not available - grass will be as child of the terrain');
 +
 +  // load the grass templates
 +  LTexPath := IncludeTrailingPathDelimiter(AAssetsPath + 'grass');
 +  FGrassTemps[0] := TGorillaModel.LoadNewModelFromFile(FViewport, nil,
 +    LTexPath + GORILLA_MODEL_GRASS_1, []);
 +  FGrassTemps[0].Visible := false; // it do not need to be shown
 +
 +  // create a list for all 64 billboard chunks
 +  FGrassBillboards := TList<TGorillaBillboard>.Create();
 +
 +  // acquire a bounding volume hierarchy for much faster raytracing
 +  // after usage, it will be destroyed, because it consumes a lot memory
 +  // and we only need it for the moment
 +  TMeshDef(ATerrain.Def).AcquireBVH();
 +  try
 +    LChunks := 64;
 +    LChunksPerRow := Ceil(Sqrt(LChunks));
 +
 +    LChunkSize.X := (MAP_SIZE / LChunksPerRow);
 +    LChunkSize.Y := MAP_HEIGHT;
 +    LChunkSize.Z := (MAP_SIZE / LChunksPerRow);
 +
 +    for i := 0 to LChunks - 1 do
 +    begin
 +      // create multiple grass billboards to use frustum culling
 +      LGrass := TGorillaBillboard.Create(FViewport);
 +      LGrass.Parent := FViewport;
 +      LGrass.Opaque := false; // allow translucent rendering
 +      LGrass.TwoSide := true;
 +      LGrass.Name := 'Grass' + IntToStr(i + 1);
 +
 +      // add the grass model as source object
 +      // multiple source objects are possible and will be chosen randomly
 +      LGrass.AddSourceObject(FGrassTemps[0]);
 +      
 +      // set the size of billboard chunk
 +      LGrass.SetSize(LChunkSize.X, LChunkSize.Y, LChunkSize.Z);
 +
 +      LX := -(MAP_SIZE / 2) + ((i mod LChunksPerRow) * LChunkSize.X);
 +      LZ := (MAP_SIZE / 2) - (Floor(i / LChunksPerRow) * LChunkSize.Z);
 +
 +      // place billboard chunk over terrain
 +      LGrass.Position.Point := Point3D(LX, 0, LZ);
 +      
 +      // take the template model material source as material for the billboard
 +      // this could be replaced by a grass material shader of course
 +      LGrass.MaterialSource := FGrassTemps[0].Meshes[0].MaterialSource;
 +      FGrassBillboards.Add(LGrass);
 +
 +      // fill up billboard chunk with 64 copies of our grass template
 +      LFillAlg := TGorillaBillboardTerrainFilling.Create(LGrass, ATerrain);
 +      try
 +        LFillAlg.Count := 64;
 +        LGrass.Fill(LFillAlg, true);
 +      finally
 +        FreeAndNil(LFillAlg);
 +      end;
 +
 +      // we put grass billboard into terrain model
 +      LGrass.Parent := ATerrain;
 +    end;
 +  finally
 +    // at the end we destroy the bounding volume hierarchy again to free memory
 +    TMeshDef(ATerrain.Def).ReleaseBVH();
 +  end;
 +end;
 +</file>
 +
 +Next step: [[inventory|Inventory]]