如何使一个部分的颜色在 3D 对象上不同

How to make color of a section be different on a 3D object

我有以下 3D 对象:

我的 3D 对象的 material 使用 Qt3D 编码如下:

void MyClass::addMaterial(Qt3DCore::QEntity *entity)
{
    Qt3DExtras::QPhongMaterial * material = new Qt3DExtras::QPhongMaterial();
    material->setAmbient(QColor(245-30, 245-15, 245));
    material->setDiffuse(QColor(125-30, 125-15, 125));
    material->setSpecular(QColor(215-30, 255-15, 255));
    entity->addComponent(material);
}

以上代码统一为3D对象实体赋予相同的颜色。如何为实体的不同部分赋予不同的颜色?我想突出显示我的 3D 对象的某些部分,有没有办法用 Qt3D 做到这一点?

如果 Qt3D 无法实现,那么使用 OpenGL 的最佳做法是什么?


我找到了一个 discussion,我觉得它会很有帮助。


经过一些研究,最好的方法可能是使用 OpenGL 着色语言或 GLSL。我不确定。

在 Qt 中,我想他们正在使用 "materials" 作为他们预定义着色器的抽象。您创建一个 material,例如 QPhongMaterial,您希望使用它来渲染实体并为其设置内容 material,它将作为参数传递给着色器(制服),例如环境光、镜面反射和漫射照明颜色.

所以您需要一个支持模型顶点颜色的着色器。所以模型的每个顶点都会有一个与之关联的颜色,这些值会传递到顶点着色器并转发到片段着色器。使用 OpenGL 执行此操作相当简单,但您使用的是 Qt3D,因此使用已有的基础结构可能更容易。

Qt3D 已经有一些 material 类 可以用来创建其中一些着色器以应用于您的实体。您可以尝试改用 QPerVertexColorMaterial。

https://doc.qt.io/qt-5/qt3dextras-qpervertexcolormaterial.html

Qt3DExtras::QPerVertexColorMaterial Default implementation for rendering the color properties set for each vertex

显然,您需要提供顶点颜色列表,以便对模型的各个部分进行不同的着色。除此之外,您还需要为每个顶点提供顶点颜色,即使您想为三角形的每个顶点都涂上相同的颜色(面颜色)。

或者,您可以创建自己的着色器并将其提供给 Qt3D 以绑定到管道: (来自论坛)

Qt3D does not generate shaders at runtime. For its default pipeline it comes with predefined default shaders. But you are free to change the pipeline as you wish (in C++ and also in QML) and you can use your own shaders.

对于自定义 materials,以下示例看起来很有希望: https://doc.qt.io/qt-5/qt3d-simplecustommaterial-example.html

关于 Qt3D 着色器的信息: https://doc.qt.io/qt-5/qml-qt3d-render-shaderprogram.html

ShaderProgram class encapsulates a shader program. A shader program consists of several different shaders, such as vertex and fragment shaders. Qt3D will automatically populate a set of default uniforms if they are encountered during the shader instrospection phase.

如果您从未在 GLSL 中编写过着色器并在 OpenGL 程序中编译过它,您可能希望首先了解如何完成,因为您需要了解顶点、几何和片段的部分着色器都扮演着属性和制服的角色。我想你仍然可以在不这样做的情况下完成工作,但我想这会更加困难。

着色器上有很多页面,但只是短暂...

一个着色器一般由三部分组成;顶点着色器、几何着色器和片段着色器,这些是使用 GLSL 编写的,通常保存为三个文件。至少要编译着色器,您需要顶点着色器源和片段着色器源。

通常人们喜欢用相应的扩展名保存这些文件,例如 .frag、.vert 或 .vs/.fs,但最终它们只是文本文件。 要编译着色器并将其绑定到渲染管道,您需要从相应的文件加载源代码并 link 它们来创建您的着色器程序,然后您可以通过将其绑定到渲染管道并渲染几何图形来使用它。 Lazy Foo 有一个关于如何在 OpenGL 中完成的很棒的教程:http://lazyfoo.net/tutorials/SDL/51_SDL_and_modern_opengl/index.php

如果您只是使用 OpenGL,您将首先编写一个顶点着色器,然后编写一个具有正确 inputs/outputs 的片段着色器来渲染具有顶点颜色的几何体,然后您将完成该过程以创建您的着色器程序.

至于着色器实现本身,这里是顶点和片段着色器的快速实现:

顶点着色器(Color.vs)

#version 330

in vec3 position;
in vec4 vertexColor;

uniform mat4 WORLD_VIEW_PROJECTION_MATRIX;

out vec4 fragColor;

void main()
{
  fragColor.x = vertexColor.x;
  fragColor.y = vertexColor.y;
  fragColor.z = vertexColor.z;
  fragColor.w = vertexColor.w; //alpha
  gl_Position = WORLD_VIEW_PROJECTION_MATRIX * vec4( position, 1 );
}

片段着色器(Color.fs)

#version 330 
out vec4 LFragment; 

in vec4 fragColor;

void main() 
{   
    LFragment = fragColor;
}

在@AdaRaider 的大力帮助下,我设法使用可修改的顶点着色器和片段着色器实现了自定义 效果。自定义效果可以这样使用:

#include "customeffect.h"

static const QColor ambientColor("#576675");  // Shader input
static const QColor diffuseColor("#5F6E7D");  // Shader input
static const QColor SpecularColor("#61707F"); // Shader input
static const float shininess(0.0);            // Shader input

void MyClass::addMaterial(Qt3DCore::QEntity *entity)
{
    Qt3DRender::QMaterial * material = new Qt3DRender::QMaterial();
    material->setEffect(new CustomEffect());
    material->addParameter(new Qt3DRender::QParameter(QStringLiteral("ka"), ambientColor));
    material->addParameter(new Qt3DRender::QParameter(QStringLiteral("kd"), diffuseColor));
    material->addParameter(new Qt3DRender::QParameter(QStringLiteral("ks"), SpecularColor));
    material->addParameter(new Qt3DRender::QParameter(QStringLiteral("shininess"), shininess));
    entity->addComponent(material);
}

自定义效果header是:

#ifndef CUSTOMEFFECT_H
#define CUSTOMEFFECT_H

#include <Qt3DRender/QEffect>

class CustomEffect : public Qt3DRender::QEffect
{
public:
    explicit CustomEffect(Qt3DCore::QNode *parent = nullptr);
};

#endif // CUSTOMEFFECT_H

自定义效果的实现考虑了 OpenGL 的两个 版本,OpenGL ES 2.0 和 OpenGL 3.1 :

#include "customeffect.h"

#include <Qt3DRender/QTechnique>
#include <Qt3DRender/QGraphicsApiFilter>
#include <QtCore/QUrl>

CustomEffect::CustomEffect(Qt3DCore::QNode *parent)
    : Qt3DRender::QEffect(parent)
{

    Qt3DRender::QTechnique *techniqueES20 = new Qt3DRender::QTechnique();
    techniqueES20->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile);
    techniqueES20->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGLES);
    techniqueES20->graphicsApiFilter()->setMajorVersion(2);
    techniqueES20->graphicsApiFilter()->setMinorVersion(0);

    Qt3DRender::QTechnique *techniqueGL31 = new Qt3DRender::QTechnique();
    techniqueGL31->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile);
    techniqueGL31->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
    techniqueGL31->graphicsApiFilter()->setMajorVersion(3);
    techniqueGL31->graphicsApiFilter()->setMinorVersion(1);

    Qt3DRender::QFilterKey *filterkey = new Qt3DRender::QFilterKey(this);
    filterkey->setName(QStringLiteral("renderingStyle"));
    filterkey->setValue(QStringLiteral("forward"));

    techniqueES20->addFilterKey(filterkey);
    techniqueGL31->addFilterKey(filterkey);

    Qt3DRender::QShaderProgram *shader2 = new Qt3DRender::QShaderProgram();
    shader2->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/qt3deditorlib/shaders/es2/custom-shader.vert"))));
    shader2->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/qt3deditorlib/shaders/es2/custom-shader.frag"))));

    Qt3DRender::QShaderProgram *shader3 = new Qt3DRender::QShaderProgram();
    shader3->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/qt3deditorlib/shaders/gl3/custom-shader.vert"))));
    shader3->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/qt3deditorlib/shaders/gl3/custom-shader.frag"))));

    Qt3DRender::QRenderPass *renderPass2 = new Qt3DRender::QRenderPass();
    renderPass2->setShaderProgram(shader2);

    Qt3DRender::QRenderPass *renderPass3 = new Qt3DRender::QRenderPass();
    renderPass3->setShaderProgram(shader3);

    techniqueES20->addRenderPass(renderPass2);
    techniqueGL31->addRenderPass(renderPass3);

    addTechnique(techniqueES20);
    addTechnique(techniqueGL31);
}

当我在我的 custom-shader.vertcustom-shader.frag 文件中复制 Phong 着色器默认代码时,它的工作方式与 Phong material 完全一样,这表明自定义效果工作正常。


既然已经实现了上面的自定义效果,那么可以通过@AdaRaider 描述的方法修改它的着色器来产生任何想要的效果。