我正在尝试使用
ShaderEffect
在 QML 中开发模拟速度计,但我未能达到预期的结果。
目标是填充着色器(红色弧形图像,称为 colorSource)在圆形参考上大约 240 度处开始,并在大约 320 度处结束。弧形图像应根据 RPM 值前进并填充圆代替针。然而,我在计算正确的开始和结束角度时遇到了困难。
meter.qml
:
Item {
id: rpmFillRough
width: 536
height: 536
layer.enabled: true
anchors.centerIn: parent
layer.effect: ShaderEffect {
readonly property real degrees: Math.PI / 180 // used for deg -> rad translations
readonly property var colorSource: rpmFill
readonly property var unfilledMarksSource: rpmMarksUnfilled
readonly property var filledMarksSource: rpmMarksFilled
property real normalizedValue: (rpm <= 10000 ? root.rpm : 10000) / 10000
readonly property real rotDeg: 215 + (-35 - 215) * normalizedValue
readonly property real rotRad: rotDeg * degrees
readonly property real startCutAngleRad: rotRad - 0.5 * degrees
readonly property real endCutAngleRad: (215 - 360 + 10) * degrees
readonly property real textureSizePx: rpmFill.width
readonly property real startCutRadius: 0.40
// rotRad is negative because it's a right-handed coord system
readonly property real s: Math.sin(-rotRad)
readonly property real c: Math.cos(-rotRad)
readonly property bool fillVisible: root.rpm > 1
fragmentShader: Shader.path("tachometerShader")
}
}
t.frag
:
uniform lowp sampler2D colorSource;
uniform lowp sampler2D unfilledMarksSource;
uniform lowp sampler2D filledMarksSource;
varying highp vec2 qt_TexCoord0;
uniform lowp float s;
uniform lowp float c;
uniform lowp float startCutRadius;
uniform lowp float textureSizePx;
uniform lowp float startCutAngleRad;
uniform lowp float endCutAngleRad;
uniform lowp float rotRad;
uniform bool fillVisible;
lowp float atanApprox(lowp float y, lowp float x) {
lowp float absX = abs(x);
lowp float absY = abs(y);
lowp float a = min(absX, absY) / max(absX, absY);
lowp float s = pow(a, 2.0);
lowp float r = ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a;
if (absY > absX)
r = 1.57079637 - r;
if (x < 0.0)
r = 3.14159274 - r;
if (y < 0.0)
r = -r;
return r;
}
void main() {
// background
gl_FragColor = texture2D(unfilledMarksSource, qt_TexCoord0);
if (!fillVisible) {
return;
}
lowp vec2 srcCoord11 = vec2(
qt_TexCoord0.x * 2. - 1.,
1. - qt_TexCoord0.y * 2.
);
// (0,0)---+ +-----(1,1)
// convert | | ----> | (0,0) |
// +---(1,1) (-1,-1)----+
// From OpenGL to school math coordinates; invert Y
// get polar coordinates of the current pixel
lowp float curAngle = atanApprox(srcCoord11.y, srcCoord11.x);
lowp float curRadius = sqrt(srcCoord11.x * srcCoord11.x + srcCoord11.y * srcCoord11.y) + 0.05;
// make bottom-left quadrant compatible with all others
if(curAngle < -2.) {
curAngle += 1.28;
}
// cut out what's not needed
if(curAngle > startCutAngleRad
&& curRadius < 1.0
&& curRadius > startCutRadius
&& fillVisible) {
lowp mat2 rotationMatrix2x2 = mat2(c, -s, s, c);
lowp vec2 destCoord = srcCoord11 * rotationMatrix2x2;
// convert to back to 0..1 system
destCoord = vec2(
.5 + .5 * destCoord.x,
.5 - .5 * destCoord.y);
lowp vec4 fill_color = texture2D(colorSource, destCoord);
gl_FragColor = mix(gl_FragColor, fill_color, fill_color.a);
lowp vec4 marks_color = texture2D(filledMarksSource, qt_TexCoord0);
gl_FragColor = mix(gl_FragColor, marks_color, marks_color.a * fill_color.a);
}
}
您可以使用两张图像来简化问题。一个来自模拟车速表背景,另一个用于车速表指针。
Image {
source: "speed-bk.png"
Image {
anchors.fill: parent
source: "speed-needle.png"
rotation: slider.value
}
}
Slider { id: slider; y: 400; from: -135; to: 135 }
您可以在线尝试!