Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpPanda3DRGBRenderer.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 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
44const 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
83const 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
173std::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
190void 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
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;
302 win_prop.set_size(m_renderParameters.getImageWidth(), m_renderParameters.getImageHeight());
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
334END_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
338void 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.
Definition of the vpImage class member functions.
Definition vpImage.h:131
unsigned int getWidth() const
Definition vpImage.h:242
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.