Visual Servoing Platform  version 3.6.1 under development (2025-03-06)
vpPanda3DRGBRenderer.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  */
30 
31 #include <visp3/ar/vpPanda3DRGBRenderer.h>
32 
33 #if defined(VISP_HAVE_PANDA3D)
34 
35 #include "orthographicLens.h"
36 #include "cardMaker.h"
37 #include "texturePool.h"
38 #include "graphicsOutput.h"
39 #include "graphicsEngine.h"
40 #include "windowFramework.h"
41 
42 
43 BEGIN_VISP_NAMESPACE
44 const std::string vpPanda3DRGBRenderer::COOK_TORRANCE_VERT =
45 "#version 330\n"
46 "in vec3 p3d_Normal;\n"
47 "in vec4 p3d_Vertex;\n"
48 "out vec3 oNormal;\n"
49 "out vec4 viewVertex;\n"
50 "uniform mat3 p3d_NormalMatrix;\n"
51 "uniform mat4 p3d_ModelViewMatrix;\n"
52 "uniform mat4 p3d_ModelViewProjectionMatrix;\n"
53 "in vec2 p3d_MultiTexCoord0;\n"
54 "out vec2 texcoords;\n"
55 "out vec3 F0;\n"
56 "uniform struct p3d_MaterialParameters {\n"
57 " vec4 ambient;\n"
58 " vec4 diffuse;\n"
59 " vec4 emission;\n"
60 " vec3 specular;\n"
61 " float shininess;\n"
62 " // These properties are new in 1.10.\n"
63 " vec4 baseColor;\n"
64 " float roughness;\n"
65 " float metallic;\n"
66 " float refractiveIndex;\n"
67 "} p3d_Material;\n"
68 "vec3 computeF0(float ior, float metallic, vec3 baseColor)\n"
69 "{\n"
70 " float F0f = pow(abs((1.0 - ior) / (1.0 + ior)), 2.0);\n"
71 " vec3 F0 = vec3(F0f, F0f, F0f);\n"
72 " return mix(F0, baseColor, metallic);\n"
73 "}\n"
74 "void main()\n"
75 "{\n"
76 " gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;\n"
77 " oNormal = p3d_NormalMatrix * normalize(p3d_Normal);\n"
78 " viewVertex = p3d_ModelViewMatrix * p3d_Vertex;\n"
79 " texcoords = p3d_MultiTexCoord0;\n"
80 " F0 = computeF0(p3d_Material.refractiveIndex, p3d_Material.metallic, p3d_Material.baseColor.xyz);\n"
81 "}\n";
82 
83 const std::string vpPanda3DRGBRenderer::COOK_TORRANCE_FRAG =
84 "// Version 330, specified when generating shader\n"
85 "#define M_PI 3.1415926535897932384626433832795\n"
86 "in vec3 oNormal;\n"
87 "in vec4 viewVertex;\n"
88 "in vec3 F0;\n"
89 "out vec4 p3d_FragData;\n"
90 "uniform struct {\n"
91 " vec4 ambient;\n"
92 "} p3d_LightModel;\n"
93 "uniform struct p3d_LightSourceParameters {\n"
94 " // Primary light color.\n"
95 " vec4 color;\n"
96 " // View-space position. If w=0, this is a directional light, with the xyz\n"
97 " // being -direction.\n"
98 " vec4 position;\n"
99 " // constant, linear, quadratic attenuation in one vector\n"
100 " vec3 attenuation;\n"
101 "} p3d_LightSource[4];\n"
102 "uniform struct p3d_MaterialParameters {\n"
103 " vec4 ambient;\n"
104 " vec4 diffuse;\n"
105 " vec4 emission;\n"
106 " vec3 specular;\n"
107 " float shininess;\n"
108 " // These properties are new in 1.10.\n"
109 " vec4 baseColor;\n"
110 " float roughness;\n"
111 " float metallic;\n"
112 " float refractiveIndex;\n"
113 "} p3d_Material;\n"
114 "in vec2 texcoords;\n"
115 "#ifdef HAS_TEXTURE\n"
116 "uniform sampler2D p3d_Texture0;\n"
117 "#endif\n"
118 "float D(float roughness2, float hn)\n"
119 "{\n"
120 " return (1.f / (M_PI * roughness2)) * pow(hn, (2.f / roughness2) - 2.f);\n"
121 "}\n"
122 "float G(float hn, float nv, float nl, float vh)\n"
123 "{\n"
124 " return min(1.0, min((2.f * hn * nv) / vh, (2.f * hn * nl) / vh));\n"
125 "}\n"
126 "vec3 F(vec3 F0, float vh)\n"
127 "{\n"
128 " return F0 + (vec3(1.f, 1.f, 1.f) - F0) * pow(1.f - vh, 5);\n"
129 "}\n"
130 "void main()\n"
131 "{\n"
132 " vec3 n = normalize(oNormal); // normalized normal vector\n"
133 " vec3 v = normalize(-viewVertex.xyz); // normalized view vector\n"
134 " float nv = max(0.f, dot(n, v));\n"
135 " float roughness2 = clamp(pow(p3d_Material.roughness, 2), 0.01, 0.99);\n"
136 
137 " #ifdef HAS_TEXTURE\n"
138 " vec4 baseColor = texture(p3d_Texture0, texcoords);\n"
139 " vec4 ambientColor = baseColor;\n"
140 " #else\n"
141 " vec4 ambientColor = p3d_Material.ambient;\n"
142 " vec4 baseColor = p3d_Material.baseColor;\n"
143 " #endif\n"
144 " p3d_FragData = p3d_LightModel.ambient * baseColor;\n"
145 " for(int i = 0; i < p3d_LightSource.length(); ++i) {\n"
146 " vec3 lf = p3d_LightSource[i].position.xyz - (viewVertex.xyz * p3d_LightSource[i].position.w);\n"
147 " float lightDist = length(lf);\n"
148 " vec3 l = normalize(lf); // normalized light vector\n"
149 " vec3 h = normalize(l + v); // half way vector\n"
150 " float hn = dot(h, n);\n"
151 " float nl = max(0.f, dot(n, l));\n"
152 " float vh = max(0.f, dot(v, h));\n"
153 " vec3 aFac = p3d_LightSource[i].attenuation;\n"
154 " float attenuation = 1.f / (aFac[0] + aFac[1] * lightDist + aFac[2] * lightDist * lightDist);\n"
155 " vec3 FV = F(F0, vh);\n"
156 " vec3 kd = (1.f - p3d_Material.metallic) * (1.f - FV) * (1.f / M_PI);\n"
157 " #ifdef SPECULAR\n"
158 " vec3 specularColor = vec3(0.f, 0.f, 0.f);\n"
159 " if(nl > 0.f && nv > 0.f) {\n"
160 " float DV = D(roughness2, hn);\n"
161 " float GV = G(hn, nv, nl, vh);\n"
162 " vec3 rs = (DV * GV * FV) / (4.f * nl * nv);\n"
163 " specularColor = rs * p3d_Material.specular;\n"
164 " }\n"
165 " #else\n"
166 " vec3 specularColor = vec3(0.0, 0.0, 0.0);\n"
167 " #endif\n"
168 " p3d_FragData += (p3d_LightSource[i].color * attenuation) * nl * (baseColor * vec4(kd, 1.f) + vec4(specularColor, 1.f));\n"
169 " }\n"
170 " p3d_FragData.bgra = p3d_FragData;\n"
171 "}\n";
172 
173 std::string vpPanda3DRGBRenderer::makeFragmentShader(bool hasTexture, bool specular)
174 {
175  std::stringstream ss;
176  ss << "#version 330" << std::endl;
177  if (hasTexture) {
178  ss << "#define HAS_TEXTURE 1" << std::endl;
179  }
180  if (specular) {
181  ss << "#define SPECULAR 1" << std::endl;
182  }
183  else {
184  ss << "#undef SPECULAR" << std::endl;
185  }
186  ss << vpPanda3DRGBRenderer::COOK_TORRANCE_FRAG;
187  return ss.str();
188 }
189 
190 void vpPanda3DRGBRenderer::addNodeToScene(const NodePath &object)
191 {
192  NodePath objectInScene = object.copy_to(m_renderRoot);
193  objectInScene.set_name(object.get_name());
194  TextureCollection txs = objectInScene.find_all_textures();
195  bool hasTexture = (static_cast<unsigned int>(txs.size()) > 0);
196  // gltf2bam and other tools may store some fallback textures. We shouldnt use them as they whiten the result
197  if (hasTexture) {
198  std::vector<std::string> fallbackNames { "pbr-fallback", "normal-fallback", "emission-fallback" };
199  unsigned int numMatches = 0;
200  for (const std::string &fallbackName: fallbackNames) {
201  numMatches += static_cast<int>(txs.find_texture(fallbackName) != nullptr);
202  }
203  hasTexture = (static_cast<unsigned int>(txs.size()) > numMatches); // Some textures are not fallback textures
204  }
205 
206  PT(Shader) shader = Shader::make(Shader::ShaderLanguage::SL_GLSL,
207  COOK_TORRANCE_VERT,
208  makeFragmentShader(hasTexture, m_showSpeculars));
209 
210  objectInScene.set_shader(shader);
211 
212  setNodePose(objectInScene, vpHomogeneousMatrix());
213 }
214 
216 {
217 
218  if (m_display2d == nullptr) {
219  CardMaker cm("card");
220  cm.set_frame_fullscreen_quad();
221 
222  NodePath myCamera2d(new Camera("myCam2d"));
223  PT(OrthographicLens) lens = new OrthographicLens();
224  lens->set_film_size(2, 2);
225  lens->set_near_far(-1000, 1000);
226  lens->set_film_offset(0, 0);
227  ((Camera *)myCamera2d.node())->set_lens(lens);
228 
229  NodePath myRender2d("myRender2d");
230  myRender2d.set_depth_test(false);
231  myRender2d.set_depth_write(false);
232  myCamera2d.reparent_to(myRender2d);
233  m_backgroundImage = myRender2d.attach_new_node(cm.generate());
234 
235  m_display2d = m_colorBuffer->make_display_region();
236  m_display2d->set_sort(-100);
237  m_display2d->set_camera(myCamera2d);
238  }
239  if (m_backgroundTexture == nullptr) {
240  m_backgroundTexture = new Texture();
241  }
242  m_backgroundImage.set_texture(m_backgroundTexture);
243  m_backgroundTexture->setup_2d_texture(background.getWidth(), background.getHeight(),
244  Texture::ComponentType::T_unsigned_byte,
245  Texture::Format::F_rgba8);
246 
247  unsigned char *data = (unsigned char *)m_backgroundTexture->modify_ram_image();
248 
249  for (unsigned int i = 0; i < background.getHeight(); ++i) {
250  const vpRGBa *srcRow = background[background.getHeight() - (i + 1)];
251  unsigned char *destRow = data + i * background.getWidth() * 4;
252  memcpy(destRow, srcRow, background.getWidth() * 4);
253  }
254 }
255 
257 {
258  I.resize(m_colorTexture->get_y_size(), m_colorTexture->get_x_size());
259  unsigned char *data = (unsigned char *)(&(m_colorTexture->get_ram_image().front()));
260  int rowIncrement = I.getWidth() * 4;
261  // Panda3D stores the image using the OpenGL convention (origin is bottom left),
262  // while we store data with origin as upper left. We copy with a flip
263  data = data + rowIncrement * (I.getHeight() - 1);
264  rowIncrement = -rowIncrement;
265 
266  for (unsigned int i = 0; i < I.getHeight(); ++i) {
267  vpRGBa *colorRow = I[i];
268 
269  memcpy((unsigned char *)(colorRow), data, sizeof(unsigned char) * 4 * I.getWidth());
270  // for (unsigned int j = 0; j < I.getWidth(); ++j) {
271  // // BGRA order in panda3d
272  // colorRow[j].R = data[j * 4];
273  // colorRow[j].G = data[j * 4 + 1];
274  // colorRow[j].B = data[j * 4 + 2];
275  // colorRow[j].A = data[j * 4 + 3];
276  // }
277  data += rowIncrement;
278  }
279 }
280 
281 
283 {
286 }
287 
289 {
290  if (m_window == nullptr) {
291  throw vpException(vpException::fatalError, "Cannot setup render target when window is null");
292  }
293  FrameBufferProperties fbp;
294  fbp.set_rgb_color(true);
295  fbp.set_float_depth(false);
296  fbp.set_float_color(false);
297  fbp.set_depth_bits(16);
298  fbp.set_rgba_bits(8, 8, 8, 8);
299  fbp.set_srgb_color(true);
300 
301  WindowProperties win_prop;
303 
304  // Don't open a window - force it to be an offscreen buffer.
305  int flags = GraphicsPipe::BF_refuse_window | GraphicsPipe::BF_resizeable;
306  GraphicsOutput *windowOutput = m_window->get_graphics_output();
307  GraphicsEngine *engine = windowOutput->get_engine();
308  GraphicsStateGuardian *gsg = windowOutput->get_gsg();
309  GraphicsPipe *pipe = windowOutput->get_pipe();
310  static int id = 0;
311  m_colorBuffer = engine->make_output(pipe, "Color Buffer" + std::to_string(id), m_renderOrder,
312  fbp, win_prop, flags,
313  gsg, windowOutput);
314  m_colorTexture = new Texture("Color texture" + std::to_string(id));
315  ++id;
316  if (m_colorBuffer == nullptr) {
317  throw vpException(vpException::fatalError, "Could not create color buffer");
318  }
319  m_buffers.push_back(m_colorBuffer);
320  //m_colorBuffer->set_inverted(gsg->get_copy_texture_inverted());
321  fbp.setup_color_texture(m_colorTexture);
322  //m_colorTexture->set_format(Texture::Format::F_srgb_alpha);
323  m_colorBuffer->add_render_texture(m_colorTexture, GraphicsOutput::RenderTextureMode::RTM_copy_texture, GraphicsOutput::RenderTexturePlane::RTP_color);
324  m_colorBuffer->set_clear_color(LColor(0.f));
325  m_colorBuffer->set_clear_color_active(true);
326  DisplayRegion *region = m_colorBuffer->make_display_region();
327  if (region == nullptr) {
328  throw vpException(vpException::fatalError, "Could not create display region");
329  }
330  region->set_camera(m_cameraPath);
331  region->set_clear_color(LColor(0.f));
332 }
333 
334 END_VISP_NAMESPACE
335 
336 #elif !defined(VISP_BUILD_SHARED_LIBS)
337 // Work around to avoid warning: libvisp_ar.a(vpPanda3DRGBRenderer.cpp.o) has no symbols
338 void dummy_vpPanda3DRGBRenderer() { };
339 
340 #endif
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ fatalError
Fatal error.
Definition: vpException.h:72
Implementation of an homogeneous matrix and operations on such kind of matrices.
unsigned int getWidth() const
Definition: vpImage.h:242
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:544
unsigned int getHeight() const
Definition: vpImage.h:181
virtual void setupScene()
Initialize the scene for this specific renderer.
NodePath m_renderRoot
Rendering parameters.
std::vector< PointerTo< GraphicsOutput > > m_buffers
NodePath of the camera.
PointerTo< WindowFramework > m_window
Rendering priority for this renderer and its buffers. A lower value will be rendered first....
int m_renderOrder
name of the renderer
virtual void setNodePose(const std::string &name, const vpHomogeneousMatrix &wTo)
Set the pose of a node. This node can be any Panda object (light, mesh, camera). The pose is specifie...
vpPanda3DRenderParameters m_renderParameters
Pointer to owning window, which can create buffers etc. It is not necessarily visible.
void setLightableScene(NodePath &scene)
virtual std::string makeFragmentShader(bool hasTexture, bool specular)
void getRender(vpImage< vpRGBa > &I) const
Store the render resulting from calling renderFrame() into a vpImage.
void addNodeToScene(const NodePath &object) VP_OVERRIDE
Add a node to the scene. Its pose is set as the identity matrix.
void setBackgroundImage(const vpImage< vpRGBa > &background)
void setupScene() VP_OVERRIDE
Initialize the scene for this specific renderer.
void setupRenderTarget() VP_OVERRIDE
Initialize buffers and other objects that are required to save the render.
Definition: vpRGBa.h:70