缩放变换与平移变换一样简单。
将顶点位置乘以想要的比例。以下是之前示例中对着色器的更改。
struct Uniforms {
color: vec4f,
resolution: vec2f,
translation: vec2f,
rotation: vec2f,
scale: vec2f,
};
struct Vertex {
@location(0) position: vec2f,
};
struct VSOutput {
@builtin(position) position: vec4f,
};
@group(0) @binding(0) var<uniform> uni: Uniforms;
@vertex fn vs(vert: Vertex) -> VSOutput {
var vsOut: VSOutput;
// Scale the position
let scaledPosition = vert.position * uni.scale; //here
// Rotate the position
let rotatedPosition = vec2f(
//vert.position.x * uni.rotation.y - vert.position.y * uni.rotation.x,
//vert.position.x * uni.rotation.x + vert.position.y * uni.rotation.y
scaledPosition.x * uni.rotation.y - scaledPosition.y * uni.rotation.x,
scaledPosition.x * uni.rotation.x + scaledPosition.y * uni.rotation.y
);
// Add in the translation
let position = rotatedPosition + uni.translation;
// convert the position from pixels to a 0.0 to 1.0 value
let zeroToOne = position / uni.resolution;
// convert from 0 <-> 1 to 0 <-> 2
let zeroToTwo = zeroToOne * 2.0;
// covert from 0 <-> 2 to -1 <-> +1 (clip space)
let flippedClipSpace = zeroToTwo - 1.0;
// flip Y
let clipSpace = flippedClipSpace * vec2f(1, -1);
vsOut.position = vec4f(clipSpace, 0.0, 1.0);
return vsOut;
}
而且,和之前前一样,需要更新uniform 缓冲区大小,以便为scale 留出空间。
// color, resolution, translation, rotation, padding
//const uniformBufferSize = (4 + 2 + 2 + 2) * 4 + 8;
// color, resolution, translation, rotation, scale
const uniformBufferSize = (4 + 2 + 2 + 2 + 2) * 4; //here
const uniformBuffer = device.createBuffer({
label: 'uniforms',
size: uniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const uniformValues = new Float32Array(uniformBufferSize / 4);
// offsets to the various uniform values in float32 indices
const kColorOffset = 0;
const kResolutionOffset = 4;
const kTranslationOffset = 6;
const kRotationOffset = 8;
const kScaleOffset = 10; //here
const colorValue = uniformValues.subarray(kColorOffset, kColorOffset + 4);
const resolutionValue = uniformValues.subarray(kResolutionOffset, kResolutionOffset + 2);
const translationValue = uniformValues.subarray(kTranslationOffset, kTranslationOffset + 2);
const rotationValue = uniformValues.subarray(kRotationOffset, kRotationOffset + 2);
const scaleValue = uniformValues.subarray(kScaleOffset, kScaleOffset + 2); //here
在渲染时需要更新缩放比例
const settings = {
translation: [150, 100],
rotation: degToRad(30),
scale: [1, 1], //here
};
const radToDegOptions = { min: -360, max: 360, step: 1, converters: GUI.converters.radToDeg };
const gui = new GUI();
gui.onChange(render);
gui.add(settings.translation, '0', 0, 1000).name('translation.x');
gui.add(settings.translation, '1', 0, 1000).name('translation.y');
gui.add(settings, 'rotation', radToDegOptions);
gui.add(settings.scale, '0', -5, 5).name('scale.x'); //here
gui.add(settings.scale, '1', -5, 5).name('scale.y'); //here
function render() {
...
// Set the uniform values in our JavaScript side Float32Array
resolutionValue.set([canvas.width, canvas.height]);
translationValue.set(settings.translation);
rotationValue.set([
Math.cos(settings.rotation),
Math.sin(settings.rotation),
]);
scaleValue.set(settings.scale); //here
// upload the uniform values to the uniform buffer
device.queue.writeBuffer(uniformBuffer, 0, uniformValues);
现在可以拖动滑块缩放。
需要注意的一件事是按负值缩放会翻转几何体。
Another thing to notice is it scales from 0, 0 which for our F is the top left corner. That makes sense since we’re multiplying the positions by the scale they will move away from 0, 0. You can probably imagine ways to fix that. For example you could add another translation before you scale, a pre scale translation. Another solution would be to change the actual F position data. We’ll go over another way soon.
另一件需要注意的事情是它从 0 开始缩放,0 对于 F 是左上角。这是合理的,因为我们将位置乘以缩放比例 将远离 0, 0 。您可能会想出解决这个问题的方法。例如,您可以在缩放之前添加另一个平移,即预缩放平移。另一种解决方案是更改实际 F 位置数据。我们很快就会采用另一种方式。
希望这最后 3 篇文章有助于理解平移、旋转和缩放。接下来,我们将回顾一下矩阵的魔力,它将所有这 3 种形式组合成一种更简单且通常更有用的形式。