如何在 Qt3D 中绘制对象的轮廓?
How do I draw the outline of an object in Qt3D?
如何在 Qt3D 中的任何其他对象之上绘制对象的轮廓?例如,要在 3D 编辑器中突出显示选定的对象?
如果你想一直画出一个实体的轮廓,即使这个实体在其他实体的后面,一种解决方案是分两步完成:
- 一切正常。
- 只绘制所选对象的轮廓。
画轮廓的时候需要用到轮廓效果,可以分两次render pass来实现:
- 使用简单的颜色着色器将几何图形渲染为纹理。
- 使用着色器渲染到屏幕,该着色器获取纹理中的每个像素并比较周围的像素。如果它们相等,则我们在对象内部并且片段可以被丢弃。如果它们不同,我们就在对象的边缘,我们应该绘制颜色。
下面是上述着色器的简单实现:
#version 150
uniform sampler2D color;
uniform vec2 winSize;
out vec4 fragColor;
void main()
{
int lineWidth = 5;
vec2 texCoord = gl_FragCoord.xy / winSize;
vec2 texCoordUp = (gl_FragCoord.xy + vec2(0, lineWidth)) / winSize;
vec2 texCoordDown = (gl_FragCoord.xy + vec2(0, -lineWidth)) / winSize;
vec2 texCoordRight = (gl_FragCoord.xy + vec2(lineWidth, 0)) / winSize;
vec2 texCoordLeft = (gl_FragCoord.xy + vec2(-lineWidth, 0)) / winSize;
vec4 col = texture(color, texCoord);
vec4 colUp = texture(color, texCoordUp);
vec4 colDown = texture(color, texCoordDown);
vec4 colRight = texture(color, texCoordRight);
vec4 colLeft = texture(color, texCoordLeft);
if ((colUp == colDown && colRight == colLeft) || col.a == 0.0)
discard;
fragColor = col;
}
注意:取值之间的差异而不是使用等式可能是更好的主意。
使用这种方法,您不必担心深度测试和绘制对象的顺序:第二次绘制时,您将始终在其他所有对象之上绘制。
您可以通过使用具有不同过滤键的两种技术添加单一效果来实现这一点。或者,如果您想使用 Qt3D.Extras 中的 material,您可以添加另一个具有相同变换和网格的实体以及使用轮廓技术的 material。
这是一个使用两个渲染过程在其他所有内容之上绘制轮廓的示例:
import QtQuick 2.2 as QQ2
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Input 2.0
import Qt3D.Extras 2.0
Entity {
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: 16/9
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 0.0, 0.0, -40.0 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
OrbitCameraController {
camera: camera
}
components: [
RenderSettings {
activeFrameGraph: RenderSurfaceSelector {
id: surfaceSelector
Viewport {
CameraSelector {
camera: camera
FrustumCulling {
TechniqueFilter {
matchAll: [
FilterKey { name: "renderingStyle"; value: "forward" }
]
ClearBuffers {
clearColor: Qt.rgba(0.1, 0.2, 0.3)
buffers: ClearBuffers.ColorDepthStencilBuffer
}
}
TechniqueFilter {
matchAll: [
FilterKey { name: "renderingStyle"; value: "outline" }
]
RenderPassFilter {
matchAny: [
FilterKey {
name: "pass"; value: "geometry"
}
]
ClearBuffers {
buffers: ClearBuffers.ColorDepthStencilBuffer
RenderTargetSelector {
target: RenderTarget {
attachments : [
RenderTargetOutput {
objectName : "color"
attachmentPoint : RenderTargetOutput.Color0
texture : Texture2D {
id : colorAttachment
width : surfaceSelector.surface.width
height : surfaceSelector.surface.height
format : Texture.RGBA32F
}
}
]
}
}
}
}
RenderPassFilter {
parameters: [
Parameter { name: "color"; value: colorAttachment },
Parameter { name: "winSize"; value : Qt.size(surfaceSelector.surface.width, surfaceSelector.surface.height) }
]
matchAny: [
FilterKey {
name: "pass"; value: "outline"
}
]
}
}
}
}
}
}
},
InputSettings { }
]
PhongMaterial {
id: material
}
Material {
id: outlineMaterial
effect: Effect {
techniques: [
Technique {
graphicsApiFilter {
api: GraphicsApiFilter.OpenGL
majorVersion: 3
minorVersion: 1
profile: GraphicsApiFilter.CoreProfile
}
filterKeys: [
FilterKey { name: "renderingStyle"; value: "outline" }
]
renderPasses: [
RenderPass {
filterKeys: [
FilterKey { name: "pass"; value: "geometry" }
]
shaderProgram: ShaderProgram {
vertexShaderCode: "
#version 150 core
in vec3 vertexPosition;
uniform mat4 modelViewProjection;
void main()
{
gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 );
}
"
fragmentShaderCode: "
#version 150 core
out vec4 fragColor;
void main()
{
fragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
}
"
}
}
]
}
]
}
}
SphereMesh {
id: sphereMesh
radius: 3
}
Transform {
id: sphereTransform
}
Transform {
id: sphereTransform2
// TODO workaround because the transform cannot be shared
matrix: sphereTransform.matrix
}
Entity {
id: sphereEntity
components: [ sphereMesh, material, sphereTransform ]
}
Entity {
id: sphereOutlineEntity
components: [ sphereMesh, outlineMaterial, sphereTransform2 ]
}
Entity {
id: outlineQuad
components: [
PlaneMesh {
width: 2.0
height: 2.0
meshResolution: Qt.size(2, 2)
},
Transform {
rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 90)
},
Material {
effect: Effect {
techniques: [
Technique {
filterKeys: [
FilterKey { name: "renderingStyle"; value: "outline" }
]
graphicsApiFilter {
api: GraphicsApiFilter.OpenGL
profile: GraphicsApiFilter.CoreProfile
majorVersion: 3
minorVersion: 1
}
renderPasses : RenderPass {
filterKeys : FilterKey { name : "pass"; value : "outline" }
shaderProgram : ShaderProgram {
vertexShaderCode: "
#version 150
in vec4 vertexPosition;
uniform mat4 modelMatrix;
void main()
{
gl_Position = modelMatrix * vertexPosition;
}
"
fragmentShaderCode: "
#version 150
uniform sampler2D color;
uniform vec2 winSize;
out vec4 fragColor;
void main()
{
int lineWidth = 5;
vec2 texCoord = gl_FragCoord.xy / winSize;
vec2 texCoordUp = (gl_FragCoord.xy + vec2(0, lineWidth)) / winSize;
vec2 texCoordDown = (gl_FragCoord.xy + vec2(0, -lineWidth)) / winSize;
vec2 texCoordRight = (gl_FragCoord.xy + vec2(lineWidth, 0)) / winSize;
vec2 texCoordLeft = (gl_FragCoord.xy + vec2(-lineWidth, 0)) / winSize;
vec4 col = texture(color, texCoord);
vec4 colUp = texture(color, texCoordUp);
vec4 colDown = texture(color, texCoordDown);
vec4 colRight = texture(color, texCoordRight);
vec4 colLeft = texture(color, texCoordLeft);
if ((colUp == colDown && colRight == colLeft) || col.a == 0.0)
discard;
fragColor = col;
}
"
}
}
}]
}
}
]
}
}
结果:
如何在 Qt3D 中的任何其他对象之上绘制对象的轮廓?例如,要在 3D 编辑器中突出显示选定的对象?
如果你想一直画出一个实体的轮廓,即使这个实体在其他实体的后面,一种解决方案是分两步完成:
- 一切正常。
- 只绘制所选对象的轮廓。
画轮廓的时候需要用到轮廓效果,可以分两次render pass来实现:
- 使用简单的颜色着色器将几何图形渲染为纹理。
- 使用着色器渲染到屏幕,该着色器获取纹理中的每个像素并比较周围的像素。如果它们相等,则我们在对象内部并且片段可以被丢弃。如果它们不同,我们就在对象的边缘,我们应该绘制颜色。
下面是上述着色器的简单实现:
#version 150
uniform sampler2D color;
uniform vec2 winSize;
out vec4 fragColor;
void main()
{
int lineWidth = 5;
vec2 texCoord = gl_FragCoord.xy / winSize;
vec2 texCoordUp = (gl_FragCoord.xy + vec2(0, lineWidth)) / winSize;
vec2 texCoordDown = (gl_FragCoord.xy + vec2(0, -lineWidth)) / winSize;
vec2 texCoordRight = (gl_FragCoord.xy + vec2(lineWidth, 0)) / winSize;
vec2 texCoordLeft = (gl_FragCoord.xy + vec2(-lineWidth, 0)) / winSize;
vec4 col = texture(color, texCoord);
vec4 colUp = texture(color, texCoordUp);
vec4 colDown = texture(color, texCoordDown);
vec4 colRight = texture(color, texCoordRight);
vec4 colLeft = texture(color, texCoordLeft);
if ((colUp == colDown && colRight == colLeft) || col.a == 0.0)
discard;
fragColor = col;
}
注意:取值之间的差异而不是使用等式可能是更好的主意。
使用这种方法,您不必担心深度测试和绘制对象的顺序:第二次绘制时,您将始终在其他所有对象之上绘制。
您可以通过使用具有不同过滤键的两种技术添加单一效果来实现这一点。或者,如果您想使用 Qt3D.Extras 中的 material,您可以添加另一个具有相同变换和网格的实体以及使用轮廓技术的 material。
这是一个使用两个渲染过程在其他所有内容之上绘制轮廓的示例:
import QtQuick 2.2 as QQ2
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Input 2.0
import Qt3D.Extras 2.0
Entity {
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: 16/9
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 0.0, 0.0, -40.0 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
OrbitCameraController {
camera: camera
}
components: [
RenderSettings {
activeFrameGraph: RenderSurfaceSelector {
id: surfaceSelector
Viewport {
CameraSelector {
camera: camera
FrustumCulling {
TechniqueFilter {
matchAll: [
FilterKey { name: "renderingStyle"; value: "forward" }
]
ClearBuffers {
clearColor: Qt.rgba(0.1, 0.2, 0.3)
buffers: ClearBuffers.ColorDepthStencilBuffer
}
}
TechniqueFilter {
matchAll: [
FilterKey { name: "renderingStyle"; value: "outline" }
]
RenderPassFilter {
matchAny: [
FilterKey {
name: "pass"; value: "geometry"
}
]
ClearBuffers {
buffers: ClearBuffers.ColorDepthStencilBuffer
RenderTargetSelector {
target: RenderTarget {
attachments : [
RenderTargetOutput {
objectName : "color"
attachmentPoint : RenderTargetOutput.Color0
texture : Texture2D {
id : colorAttachment
width : surfaceSelector.surface.width
height : surfaceSelector.surface.height
format : Texture.RGBA32F
}
}
]
}
}
}
}
RenderPassFilter {
parameters: [
Parameter { name: "color"; value: colorAttachment },
Parameter { name: "winSize"; value : Qt.size(surfaceSelector.surface.width, surfaceSelector.surface.height) }
]
matchAny: [
FilterKey {
name: "pass"; value: "outline"
}
]
}
}
}
}
}
}
},
InputSettings { }
]
PhongMaterial {
id: material
}
Material {
id: outlineMaterial
effect: Effect {
techniques: [
Technique {
graphicsApiFilter {
api: GraphicsApiFilter.OpenGL
majorVersion: 3
minorVersion: 1
profile: GraphicsApiFilter.CoreProfile
}
filterKeys: [
FilterKey { name: "renderingStyle"; value: "outline" }
]
renderPasses: [
RenderPass {
filterKeys: [
FilterKey { name: "pass"; value: "geometry" }
]
shaderProgram: ShaderProgram {
vertexShaderCode: "
#version 150 core
in vec3 vertexPosition;
uniform mat4 modelViewProjection;
void main()
{
gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 );
}
"
fragmentShaderCode: "
#version 150 core
out vec4 fragColor;
void main()
{
fragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
}
"
}
}
]
}
]
}
}
SphereMesh {
id: sphereMesh
radius: 3
}
Transform {
id: sphereTransform
}
Transform {
id: sphereTransform2
// TODO workaround because the transform cannot be shared
matrix: sphereTransform.matrix
}
Entity {
id: sphereEntity
components: [ sphereMesh, material, sphereTransform ]
}
Entity {
id: sphereOutlineEntity
components: [ sphereMesh, outlineMaterial, sphereTransform2 ]
}
Entity {
id: outlineQuad
components: [
PlaneMesh {
width: 2.0
height: 2.0
meshResolution: Qt.size(2, 2)
},
Transform {
rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 90)
},
Material {
effect: Effect {
techniques: [
Technique {
filterKeys: [
FilterKey { name: "renderingStyle"; value: "outline" }
]
graphicsApiFilter {
api: GraphicsApiFilter.OpenGL
profile: GraphicsApiFilter.CoreProfile
majorVersion: 3
minorVersion: 1
}
renderPasses : RenderPass {
filterKeys : FilterKey { name : "pass"; value : "outline" }
shaderProgram : ShaderProgram {
vertexShaderCode: "
#version 150
in vec4 vertexPosition;
uniform mat4 modelMatrix;
void main()
{
gl_Position = modelMatrix * vertexPosition;
}
"
fragmentShaderCode: "
#version 150
uniform sampler2D color;
uniform vec2 winSize;
out vec4 fragColor;
void main()
{
int lineWidth = 5;
vec2 texCoord = gl_FragCoord.xy / winSize;
vec2 texCoordUp = (gl_FragCoord.xy + vec2(0, lineWidth)) / winSize;
vec2 texCoordDown = (gl_FragCoord.xy + vec2(0, -lineWidth)) / winSize;
vec2 texCoordRight = (gl_FragCoord.xy + vec2(lineWidth, 0)) / winSize;
vec2 texCoordLeft = (gl_FragCoord.xy + vec2(-lineWidth, 0)) / winSize;
vec4 col = texture(color, texCoord);
vec4 colUp = texture(color, texCoordUp);
vec4 colDown = texture(color, texCoordDown);
vec4 colRight = texture(color, texCoordRight);
vec4 colLeft = texture(color, texCoordLeft);
if ((colUp == colDown && colRight == colLeft) || col.a == 0.0)
discard;
fragColor = col;
}
"
}
}
}]
}
}
]
}
}
结果: