本文标题:WebGL第九课:搞一搞 vertex shader(3) 拉伸和平移
首先给出拉伸和平移的定义,没有定义,我们就没法得出正确的逻辑:
拉伸:每一个点的x坐标或者y坐标都乘以一个相同的数。这个数我们称之为拉伸系数。
讲解:
要点1: 注意是每一个点,并不是某些点,或者某几个点。
要点2:拉伸有两个方向,`x方向`和`y方向`。
对于`x方向`来说,每一个点的`x坐标`必须乘以同一个数,才能叫拉伸。
`y方向`的拉伸同理。`x和y方向`,两个方向本身可以是`独立的`。也就是说我们会有两个`拉伸系数`。
要点3:系数可以是正的也可以是负的。
平移:每一个点的x坐标或者y坐标都加一个相同的数。这个数我们称之为平移系数。
讲解:
要点1: 注意是每一个点,并不是某些点,或者某几个点。
要点2:平移也是两个方向,`x方向`和`y方向`,对于`x方向`来说,每一个点的`x坐标`必须加上同一个数,才能叫平移。
`y方向`的平移同理。`x和y方向`,两个方向本身可以是`独立的`。也就是说我们也是有两个`平移系数`。
要点3:系数可以是正的也可以是负的。
根据和上面对于拉伸和平移的定义,我们更新我们整体的代码如下:
<!doctype html>
<html>
<head>
<style>
canvas {
border: 1px solid #000000;
}
</style>
</head>
<body>
<p>
<b>scale value:</b>
<input id="scalex" type="range" min="-1" max="1" value="0" step="0.1" oninput="updatefunc()" />
<b id="scalevaluex">0</b>
<input id="scaley" type="range" min="-1" max="1" value="0" step="0.1" oninput="updatefunc()" />
<b id="scalevaluey">0</b>
</p>
<p>
<b>offset value:</b>
<input id="offsetx" type="range" min="-1" max="1" value="0" step="0.1" oninput="updatefunc()" />
<b id="offsetvaluex">0</b>
<input id="offsety" type="range" min="-1" max="1" value="0" step="0.1" oninput="updatefunc()" />
<b id="offsetvaluey">0</b>
</p>
<canvas id="point" style="width:300px; height:300px">
</canvas>
<script id="vertex_shader" type="myshader">
// Vertex Shader
precision mediump int;
precision mediump float;
uniform float u_x_scale;
uniform float u_x_offset;
uniform float u_y_scale;
uniform float u_y_offset;
attribute vec2 a_PointVertex;
void main() {
gl_Position = vec4(a_PointVertex, 0.0, 1.0);
gl_Position.x *= u_x_scale;
gl_Position.x += u_x_offset;
gl_Position.y *= u_y_scale;
gl_Position.y += u_y_offset;
gl_PointSize = 3.0;
}
</script>
<script id="fragment_shader" type="myshader">
// Fragment shader
precision mediump int;
precision mediump float;
void main() {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
</script>
<script type="text/javascript">
var pointCanvas = document.getElementById('point'); // 我们的纸
var gl = pointCanvas.getContext('webgl', { preserveDrawingBuffer: true }); // 我们的笔
var pointCount = 0;
var pointData = [];
for (var idx = -5; idx <= 5; idx++) {
for (var idy = -5; idy <= 5; idy++) {
pointCount++;
pointData.push(idx / 5);
pointData.push(idy / 5);
}
}
//
var pointArray = new Float32Array(pointData);
var buffer_id;
buffer_id = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer_id);
gl.bufferData(gl.ARRAY_BUFFER, pointArray, gl.STATIC_DRAW);
//
var vertex_shader_code = document.getElementById('vertex_shader').textContent;
console.log(vertex_shader_code);
var vertex_shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertex_shader, vertex_shader_code);
gl.compileShader(vertex_shader);
//
var fragment_shader_code = document.getElementById('fragment_shader').textContent;
var fragment_shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragment_shader, fragment_shader_code);
gl.compileShader(fragment_shader);
//
var program = gl.createProgram();
gl.attachShader(program, vertex_shader);
gl.attachShader(program, fragment_shader);
gl.linkProgram(program);
gl.useProgram(program);
//
var a_PointVertex = gl.getAttribLocation(program, 'a_PointVertex');
gl.vertexAttribPointer(a_PointVertex, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_PointVertex);
//
gl.drawArrays(gl.POINTS, 0, pointCount);
</script>
<script>
var u_x_offset_loc = gl.getUniformLocation(program, "u_x_offset");
var u_x_scale_loc = gl.getUniformLocation(program, "u_x_scale");
var u_y_offset_loc = gl.getUniformLocation(program, "u_y_offset");
var u_y_scale_loc = gl.getUniformLocation(program, "u_y_scale");
var scaleDomX = document.getElementById("scalex");
var scaleValueDomX = document.getElementById("scalevaluex");
var scaleDomY = document.getElementById("scaley");
var scaleValueDomY = document.getElementById("scalevaluey");
var offsetDomX = document.getElementById("offsetx");
var offsetValueDomX = document.getElementById("offsetvaluex");
var offsetDomY = document.getElementById("offsety");
var offsetValueDomY = document.getElementById("offsetvaluey");
function updatefunc() {
gl.uniform1f(u_x_scale_loc, parseFloat(scaleDomX.value));
gl.uniform1f(u_y_scale_loc, parseFloat(scaleDomY.value));
gl.uniform1f(u_x_offset_loc, parseFloat(offsetDomX.value));
gl.uniform1f(u_y_offset_loc, parseFloat(offsetDomY.value));
scaleValueDomX.innerText = scaleDomX.value;
offsetValueDomX.innerText = offsetDomX.value;
scaleValueDomY.innerText = scaleDomY.value;
offsetValueDomY.innerText = offsetDomY.value;
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, pointCount);
}
</script>
</script>
</body>
</html>
在浏览器里直接打开这个页面,效果如下:
大家可以拖动四个slider,来看看都有什么效果。
大概的逻辑就是:
我们一共用了四个uniform变量来分别接收
x方向的拉伸值:`u_x_scale`
y方向的拉伸值:`u_y_scale`
x方向的平移值:`u_x_offset`
y方向的平移值:`u_y_offset`
并且在`html`里用了四个`slider`来控制这四个变量。
以上东西都没有涉及到新知识点,这里我们还是将重点放在 vertex shader 里, 如下:
<script id="vertex_shader" type="myshader">
// Vertex Shader
precision mediump int;
precision mediump float;
uniform float u_x_scale;
uniform float u_x_offset;
uniform float u_y_scale;
uniform float u_y_offset;
attribute vec2 a_PointVertex;
void main() {
gl_Position = vec4(a_PointVertex, 0.0, 1.0);
gl_Position.x *= u_x_scale;
gl_Position.x += u_x_offset;
gl_Position.y *= u_y_scale;
gl_Position.y += u_y_offset;
gl_PointSize = 3.0;
}
</script>
在上面的代码里,我们发现一个东西,对于两个方向的拉伸和平移来说,我们都把
计算拉伸的代码放在前面,计算平移的代码放在后面。
这是随意这么写的吗,还是说只能这么写,拉伸难道只能放在平移前面???
关于拉伸和平移的先后顺序
事实上,这取决于你的业务需求,一般的业务需求,和人们的一般感受,我们先拉伸再平移,是合乎正常人的想象的。
如果你先平移再拉伸的话,会有出其不意的效果发生,不信你可以自己将上面代码中的顺序变一下。自己做做实验。
对于百分之九十九点九九九的场景里,先拉伸再平移,相信我,就这么办!!!
正文结束,下面是答疑