iMSTK
Interactive Medical Simulation Toolkit
Docs/Rendering.md
1 # Overview
2 
3 VTK is the primary rendering backend for iMSTK, though it can be used without rendering/standalone. This also allows one to utilize any rendering backend they wish.
4 
5 A VTKViewer is used to render an iMSTK Scene. Setup as:
6 
7 ```cpp
8 auto viewer = std::make_shared<VTKViewer>();
9 viewer->setActiveScene(myScene);
10 ```
11 
12 Given the scene it sets up RenderDelegates for every VisualModel. While a VisualModel model gives what is to be rendered. The RenderDelegate implements it. Keeping rendering backend separate from iMSTK. Typically the VTKViewer is given to the SimulationManager to update. But if one would instead like to invoke render themselves. Just call update on the viewer.
13 
14 ```cpp
15 viewer->update(); // Renders now
16 ```
17 
18 ## Camera
19 
20 Cameras in iMSTK are used for rendering. They must be given to the scene and set as active. There are default cameras with the iMSTK scene. Camera's allow control with (position, focal point, up) or by view matrix directly.
21 
22 ```cpp
23 // Often the default active camera from the scene is used
24 scene->getActiveCamera()->setFocalPoint(0.0, 0.0, 0.0);
25 scene->getActiveCamera()->setPosition(-0.0237419, 0.0368787, 0.338374);
26 scene->getActiveCamera()->setViewUp(0.0, 1.0, 0.0);
27 ```
28 
29 OR
30 
31 ```cpp
32 // Camera transition over time t
33 Mat4d initView = ...;
34 Mat4d finalView = ...;
35 getActiveCamera()->setView(t * (finalView - initView) + initView);
36 ```
37 
38 Additionally the fov can change.
39 
40 ```cpp
41 camera->setFieldOfView(90.0); // Degrees
42 ```
43 
44 A camera also has a near and far. That is how close and far it can render. These are not adjusted automatically at any point in iMSTK.
45 ```cpp
46 camera->setNearZ(0.01);
47 camera->setFarZ(1000.0);
48 ```
49 
50 Quickly get the forward direction with `Camera::getForward`
51 
52 Ask the `Camera` for a ray direction emanating from where the user has clicked:
53 
54 ```cpp
55 // Get mouse position (0, 1) with origin at bot left of screen
56 const Vec2d mousePos = viewer->getMouseDevice()->getPos();
57 // Convert to (-1,1)
58 const Vec3d normalizedMousePos = Vec2d(mousePos[0] * 2.0 - 1.0, mousePos[1] * 2.0 - 1.0);
59 const Vec3d rayDir = scene->getActiveCamera()->getEyeRayDir(normalizedMousePos);
60 const Vec3d rayStart = scene->getActiveCamera()->getPosition();
61 ```
62 
63 ## RenderMaterial
64 
65 RenderMaterials give properties of VisualModel to be rendered. Importantly we have a DisplayMode
66 
67 - Surface: Renders only the surface of the geometry.
68 - Wireframe: Renders only the wireframe of the geometry, edges of the cells.
69 - Points: Renders only the vertices of the geometry.
70 - WireframeSurface: Renders both the surface of the cells
71 
72 As well as a choice between ShadingModels:
73 - Phong: Computes lighting with normals per every fragments/pixels.
74 - Gourand: Computes lighting with normals per vertex then interpolates over fragments/pixels.
75 - Flat: Computes lighting per cell and uses it for every fragment/pixel of that cell.
76 - PBR: Uses phong shading but has a very specific model based on realistic parameters.
77 
78 To setup a basic material we can do:
79 
80 ```cpp
81 RenderMaterial mat;
82 mat.setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
83 mat.setLineWidth(2.0);
84 mat.setEdgeColor(Color::Orange);
85 ```
86 
87 This gives us orange edges, gray default surface color, and edges that are displayed with size 2. The typical phong model allows us to specify specular, diffuse, and ambient lighting. We give scales and colors for each.
88 
89 ### Physically Based Rendering (PBR)
90 
91 Alternatively utilize PBR which is parameterized differently based on realistic parameters such as "roughness" or "metalness". There are also a number of other features. Here is how to use that with textures for diffuse, normals, and ambient occlusion.
92 
93 ```cpp
94 auto material = std::make_shared<RenderMaterial>();
95 material->setDisplayMode(RenderMaterial::DisplayMode::Surface);
96 material->setShadingModel(RenderMaterial::ShadingModel::PBR);
97 auto headDiffuseTexture = std::make_shared<Texture>(iMSTK_DATA_ROOT "head/HeadTexture_BaseColor.png", Texture::Type::Diffuse);
98 auto headNormalTexture = std::make_shared<Texture>(iMSTK_DATA_ROOT "head/HeadTexture_Normal.png", Texture::Type::Normal);
99 auto headAoTexture = std::make_shared<Texture>(iMSTK_DATA_ROOT "head/HeadTexture_AO.png", Texture::Type::AmbientOcclusion;
100 
101 material->setRoughness(10.0);
102 material->setMetalness(0.0);
103 material->addTexture(headDiffuseTexture);
104 material->addTexture(headNormalTexture);
105 material->addTexture(headAoTexture);
106 ```
107 
108 <p align="center">
109  <img src="media/pbr.jpg" alt="Physically based rendering" width="600"/>
110 </p>
111 
112 
113 ### Optimization
114 
115 Two important flags of this material that are by default on.
116 
117 - RenderMaterial::setRecomputeVertexNormals: When on the vertex buffer is continuously updated.
118 - RenderMaterial::setIsDynamicMesh: When off buffers aren't not even checked for changes. A transform can still be applied through the shader.
119 
120 ### DisplayMode::SurfaceNormals
121 
122 This display mode can be used to quickly render face windings.
123 
124 <p align="center">
125  <img src="media/surfaceNormals.jpg" alt="Physically based rendering" width="600"/>
126 </p>
127 
128 ### DisplayMode::Fluid
129 
130 This display mode uses screen-space fluids to render.
131 
132 <p align="center">
133  <img src="media/PbdModel/blood.png" alt="Physically based rendering" width="600"/>
134 </p>
135 
136 ## DebugGeometryModel
137 
138 The `DebugGeometryModel` can be used to quickly render primitives.
139 
140 ```cpp
141 auto debugGeomVisuals = mySceneObject->addComponent<DebugGeometryModel>();
142 debugGeomVisuals->addPoint(myVertex, myColor);
143 debugGeomVisuals->addLine(...);
144 debugGeomVisuals->addTriangle(...);
145 ```
146 
147 ## CollisionDataDebugModel
148 
149 The `CollisionDataDebugModel` extends the `DebugGeometryModel` providing quick rendering of `CollisionData` from a `CollisionDetectionAlgorithm`.
150 
151 ```cpp
152 SpheretoSphereCD detector;
153 ...
154 detector.update();
155 
156 auto cdDebugModel = mySceneObject->addComponent<CollisionDataDebugModel>();
157 cdDebugModel->setInputCD(detector.getCollisionData());
158 ```
159 
160 ## TextVisualModel
161 
162 `TextVisualModel` can be used in iMSTK to render text to the corners or center of the screen:
163 
164 ```cpp
165 auto txtVisuals = mySceneObject->addComponent<TextVisualModel>();
166 txtVisuals->setPosition(TextVisualModel::DisplayPosition::UpperLeft);
167 txtVisuals->setFontSize(30.0);
168 txtVisuals->setTextColor(Color::Orange);
169 ```
170 
171 ## Ghost Rendering
172 
173 Given a `PbdObjectController` one can quickly render the ghost of the controller with `ObjectControllerGhost` as follows:
174 
175 ```cpp
176 // Add extra component to tool for the ghost
177 auto controllerGhost = lapTool->addComponent<ObjectControllerGhost>();
178 controllerGhost->setUseForceFade(true);
179 controllerGhost->setController(controller);
180 ```
181 
182 <p align="center">
183  <img src="media/rbd1.gif" alt="Virtual coupling in action" width="800"/>
184 </p>
185 
186 ## Force Text Rendering
187 
188 It can often be useful to see the forces experienced by the user. This can be done easily by adding a `ControllerForceText` component.
189 
190 ```cpp
191 auto controllerForceTxt = mySceneObject->addComponent<ControllerForceText>();
192 controllerForceTxt->setController(controller);
193 ```
194 
195 ## Per Vertex Labelings
196 
197 The `VertexLabelVisualModel` was introduce to allow quick debugging and numbering of vertex indices/numberings. It can be configured like so:
198 
199 ```cpp
200 auto vertexLabelModel = mySceneObject->addComponent<VertexLabelVisualModel>();
201 vertexLabelModel->setGeometry(myMeshToRender);
202 ```
203 
204 <p align="center">
205  <img src="media/vertexLabels.png" alt="Vertex labeling" width="600"/>
206 </p>
207 
208 ## AxesModel
209 
210 The AxesModel can be used to quickly render a axes basis.
211 
212 ```cpp
213 auto axesModel = mySceneObject->addComponent<AxesModel>();
214 axesModel->setPosition(Vec3d(0.0, 20.0, 4.0));
215 axesModel->setPosition(Quatd::FromTwoVectors(Vec3d(0.0, 1.0, 0.0), Vec3d(1.0, 1.0, 0.0)));
216 ```
217 
218 ## VolumeRenderMaterial
219 
220 The VolumeRenderMaterial exposes VTK objects for rendering, it can be used as:
221 
222 ```cpp
223 auto material = std::make_shared<VolumeRenderMaterial>();
224 vtkNew<vtkColorTransferFunction> colorFunc;
225 colorFunc->AddRGBPoint(0.0, 1.0, 0.0, 0.0);
226 colorFunc->AddRGBPoint(8.0, 0.0, 0.0, 1.0);
227 volumeMaterial->getVolumeProperty()->SetColor(colorFunc);
228 vtkNew<vtkPiecewiseFunction> opacityFunc;
229 opacityFunc->AddPoint(0.0, 0.0);
230 opacityFunc->AddPoint(1.0, 1.0);
231 volumeMaterial->getVolumeProperty()->SetScalarOpacity(opacityFunc);
232 ```
233 
234 Here set both a color and opacity function. As we render the volume (by marching along a ray shot from the screen) we sample the image we are rendering and lookup a color and opacity. These functions specify this. AddRGBPoint(intensity, r, g, b) and AddPoint(intensity, opacity).
235 
236 A number of presets (which may or may not fit your image/volume) are also available which can be used as below:
237 
238 ```cpp
239 std::shared_ptr<VolumeRenderMaterial> volumeMaterial = VolumeRenderMaterialPresets::getPreset(currMatId);
240 ```
241 
242 <p align="center">
243  <img src="media/vhp.png" alt="Femur Signed Distance Function" width="600"/>
244 </p>
245 
246 ## Custom RenderDelegates & VisualModels
247 
248 If one needs to implement their own rendering capabilities the VisualModel's and RenderDelegates can be extended. Extending the VisualModel the delegate hint can be provided.
249 
250 ```cpp
251 class ChartVisualModel : public VisualModel
252 {
253 public:
254  ChartVisualModel()
255  {
256  // Setup delegate hint here
257  setDelegateHint("Chart");
258  }
259  ~ChartVisualModel() override = default;
260 ```
261 
262 If iMSTK is provided a delegate hint in a VisualModel it will first look to construct the corresponding RenderDelegate using this hint. iMSTK, by default, has many registrations. One can add a registration in their own code by defining a `VTKChartRenderDelegate` and adding:
263 
264 ```cpp
265 RenderDelegateRegistrar<VTKChartRenderDelegate> registerChartDelegate("Chart");
266 ```
267 
268 ## Lights
269 
270 iMSTK provides three types of lights.
271  - PointLight: Specified with a position, intensity, color.
272  - DirectionalLight: Specified with a direction, intensity, color.
273  - SpotLight: Specified with a position, focal point, cone angle, intensity, color.
274 
275 ```cpp
276 // Light
277 auto light = std::make_shared<SpotLight>();
278 light->setFocalPoint(Vec3d(0.0, 0.0, 0.0));
279 light->setPosition(Vec3d(0.0, 10.0, 0.0));
280 light->setIntensity(1.0);
281 light->setSpotAngle(10.0);
282 scene->addLight("light0", light);
283 ```
284 
285 All lights can also work with constant, linear, and quadratic falloff.
286 
287 ```cpp
288 light->setAttenuationValues(0.0, 0.0, 1.0); // Constant a
289 light->setAttenuationValues(0.0, 0.5, 0.0); // Linear falloff a + bx
290 light->setAttenuationValues(50.0, 0.0, 0.0); // Quadratic a + bx + cx^2
291 ```
292 
293 ![type:video](./media/quadratic_lights.mp4)